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

docs(recipe): Add comments for precompiling multiple test files #1385

Merged
merged 3 commits into from
Jun 17, 2017

Conversation

sudo-suhas
Copy link
Contributor

Adds comments for pre-compilation of multiple test files which ava supports. Also changed the target output directory to absolute path in accordance with webpack docs

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 the PR @sudo-suhas. I've left some inline comments.

target: 'node',
output: {
path: '_build',
path: path.resolve(__dirname, '_build'),
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure the sample code needs to prescribe this. What does webpack show in its examples?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the webpack docs, the target directory for all output files must be an absolute path (use the Node.js path module). You can see this in the code example here - https://webpack.js.org/configuration/

Copy link
Member

Choose a reason for hiding this comment

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

👍

const nodeExternals = require('webpack-node-externals');

module.exports = {
entry: ['src/tests.js'],
// entry: glob.sync('./test/**/*.js'),
Copy link
Member

Choose a reason for hiding this comment

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

The recipe discusses precompiling source files. I'm not sure why the entry here is src/tests.js but it seems even more incongruent to add test/**/*.js entries.

How are you using this?

Perhaps the globbing approach can be mentioned in the paragraph that follows the example code, with a link to the Stack Overflow post?

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 not sure why the entry here is src/tests.js

Perhaps the entry should be changed to tests/index.js?

How are you using this?

I am using this in elastic-builder. You can see the improved test duration here - https://travis-ci.org/sudo-suhas/elastic-builder/builds.

Perhaps the globbing approach can be mentioned in the paragraph that follows the example code, with a link to the Stack Overflow post?

I wasn't entirely sure about how to present it. I am okay with this. Shall I push a commit for this?

Copy link
Contributor

Choose a reason for hiding this comment

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

The entry is the test files, because they in turn include the source files that need to be precompiled. No reason precompiling files that aren't ever tested.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, the bundle in this case will be the test files and all the source files that are included in those tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

@sudo-suhas Also, am I reading that CI output correctly? 31 min 39 sec to 5 min 49 sec!? Wow!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not exactly.. It is the combined time for execution in 3 node envs(in parallel) .. But the test duration did go down from nearly 13 mins of 100% resource usage to under 3 mins in one run.

Do you think it would be more appropriate for this recipe to discuss multiple test files transpilation by default? I understand it can also be used for cases where you have aliases too. But to me it seems like the former is the more prominent use case.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I think this is a good changeset. It's far more common to have a bunch of test files that you would want to glob for.

@novemberborn
Copy link
Member

What confuses me is that the recipe talks about precompiling source files, but then the example precompiles a test file, which is how you're using it. @danny-andrews what was the original intent? I can't quite decide how this is meant to be used based on #1212 and #1011.

@sudo-suhas I think we need to clear this up (might just be that the recipe title is misleading). But then yes let's add the globbing approach in the paragraph after the example.

@sudo-suhas
Copy link
Contributor Author

@novemberborn I could be wrong, but my understanding is, it is precompiling source files. Only indirectly. So in my case, I had many independent test files. And had to use babel-register for running the tests in node 4. This proves to be very resource intensive as you might notice from the test durations. So the suggested approach is to precompile test files. Which would in turn precompile the source files because they are being referenced. One more thing I would like to add here is that if we were to indeed directly precompile src files, we would need to reference _build in test files. I think that is less elegant.

@novemberborn
Copy link
Member

Thanks for the clarification @danny-andrews.

@sudo-suhas I think you're right that this recipe could use globbing as standard. Should it output a file for each entry though? Otherwise all tests run in the same process.

@sudo-suhas
Copy link
Contributor Author

@novemberborn If I wanted to output multiple files, how would I do it? Take the basename for each file? Also, if it performs well, do we care if the tests run in the same process?

@novemberborn
Copy link
Member

I haven't used webpack that much, so I don't know the exact syntax, but I think it should be possible.

It can matter if tests modify globals Node.js built-ins. You wouldn't want that to leak across tests. There's a lot of nuance here, I'd be happy if we can clarify the specific use cases for which this approach is safe.

@danny-andrews
Copy link
Contributor

@novemberborn Does ava parallelize tests across files or tests?

@novemberborn
Copy link
Member

@danny-andrews both. Each file runs in its own process, configurably in parallel. And all test are started at the same time, so asynchronous tests also run in parallel.

@danny-andrews
Copy link
Contributor

I'll put together a sample github repo sometime this week, so we can play around with the webpack config and demonstrate an example use-case.

@sudo-suhas
Copy link
Contributor Author

The following webpack config outputs a file for each entry:

'use strict';

const path = require('path');
const glob = require('glob');

const nodeExternals = require('webpack-node-externals');

const testFiles = glob.sync('./test/**/*.js');

const entryObj = {};
for (let idx = 0; idx < testFiles.length; idx++) {
    const file = testFiles[idx];
    entryObj[path.basename(file, path.extname(file))] = file;
}

module.exports = {
    target: 'node',
    entry: entryObj,
    output: {
        path: path.resolve(__dirname, '_build'),
        filename: '[name].js'
    },
    externals: [nodeExternals()],
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                use: {
                    loader: 'babel-loader',
                    // Adding this saved ~9 seconds for me
                    options: {
                        cacheDirectory: true
                    }
                }
            }
        ]
    }
};

But this negates any performance benefit because each entry has its own copy of the src. CommonsChunkPlugin cannot be used because it is only meant to be used with the browser and not when the target is node.

If I add the commons plugin:

    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'commons',
            filename: 'commons.js',
            minChunks: 3
        })
    ]

Every test file fails:

> cross-env NODE_ENV=test nyc --cache ava _build --concurrency 3

  125 exceptions

  × No tests found in _build\aggregation.test.js, make sure to import "ava" at the top of your test file

  × No tests found in _build\avg-agg.test.js, make sure to import "ava" at the top of your test file

  × No tests found in _build\avg-bucket-agg.test.js, make sure to import "ava" at the top of your test file

Specifying entry for src and using that in the plugin doesn't help either.

@novemberborn
Copy link
Member

But this negates any performance benefit because each entry has its own copy of the src.

Starting the worker processes may still be faster, even if the source is duplicated on disk.

@sudo-suhas
Copy link
Contributor Author

sudo-suhas commented May 29, 2017

Not entirely sure which part of the setup is responsible for this but there is a huge difference in execution time between 1st(no cache) and subsequent runs.

Here's the test run timings:

test time
single entry, 1st run 0m44.255s
single entry, next run 0m23.667s
multiple entry, 1st run 17m32.462s
multiple entry, next run 1m59.068s
Webpack output(single entry)
Hash: 8898e462dc944831f19e
Version: webpack 2.5.1
Time: 12228ms
   Asset     Size  Chunks                    Chunk Names
tests.js  3.63 MB       0  [emitted]  [big]  main
   [5] ./test/_macros.js 21 kB {0} [built]
 [146] ./test/queries-test/match-phrase-prefix-query.test.js 418 bytes {0} [built]
 [170] ./test/queries-test/span-multi-term-query.test.js 698 bytes {0} [built]
 [171] ./test/queries-test/span-near-query.test.js 783 bytes {0} [built]
 [172] ./test/queries-test/span-not-query.test.js 680 bytes {0} [built]
 [173] ./test/queries-test/span-or-query.test.js 685 bytes {0} [built]
 [174] ./test/queries-test/span-term-query.test.js 780 bytes {0} [built]
 [175] ./test/queries-test/span-within-query.test.js 245 bytes {0} [built]
 [176] ./test/queries-test/term-query.test.js 693 bytes {0} [built]
 [177] ./test/queries-test/terms-query.test.js 1.97 kB {0} [built]
 [178] ./test/queries-test/type-query.test.js 419 bytes {0} [built]
 [179] ./test/queries-test/weight-score-func.test.js 346 bytes {0} [built]
 [180] ./test/queries-test/wildcard-query.test.js 664 bytes {0} [built]
 [181] ./test/recipes.test.js 8.62 kB {0} [built]
 [292] multi ./test/_macros.js ./test/aggregations-test/avg-agg.test.js ./test/aggregations-test/avg-bucket-agg.test.js ./test/aggregations-test/bucket-agg-base.test.js ./test/aggregations-test/bucket-script-agg.test.js ./test/aggregations-test/bucket-selector-agg.test.js ./test/aggregations-test/cardinality-agg.test.js ./test/aggregations-test/children-agg.test.js ./test/aggregations-test/cumulative-sum-agg.test.js ./test/aggregations-test/date-histogram-agg.test.js ./test/aggregations-test/date-range-agg.test.js ./test/aggregations-test/derivative-agg.test.js ./test/aggregations-test/diversified-sampler-agg.test.js ./test/aggregations-test/extended-stats-agg.test.js ./test/aggregations-test/extended-stats-bucket-agg.test.js ./test/aggregations-test/filter-agg.test.js ./test/aggregations-test/filters-agg.test.js ./test/aggregations-test/geo-bounds-agg.test.js ./test/aggregations-test/geo-centroid-agg.test.js ./test/aggregations-test/geo-distance-agg.test.js ./test/aggregations-test/geo-hash-grid-agg.test.js./test/aggregations-test/global-agg.test.js ./test/aggregations-test/histogram-agg-base.test.js ./test/aggregations-test/histogram-agg.test.js ./test/aggregations-test/ip-range-agg.test.js ./test/aggregations-test/matrix-stats-agg.test.js ./test/aggregations-test/max-agg.test.js ./test/aggregations-test/max-bucket-agg.test.js ./test/aggregations-test/metrics-agg-base.test.js ./test/aggregations-test/min-agg.test.js ./test/aggregations-test/min-bucket-agg.test.js ./test/aggregations-test/missing-agg.test.js./test/aggregations-test/moving-average-agg.test.js ./test/aggregations-test/nested-agg.test.js ./test/aggregations-test/percentile-ranks-agg.test.js ./test/aggregations-test/percentiles-agg.test.js ./test/aggregations-test/percentiles-bucket-agg.test.js ./test/aggregations-test/pipeline-agg-base.test.js ./test/aggregations-test/range-agg-base.test.js ./test/aggregations-test/range-agg.test.js ./test/aggregations-test/reverse-nested-agg.test.js ./test/aggregations-test/sampler-agg.test.js ./test/aggregations-test/scripted-metric-agg.test.js ./test/aggregations-test/serial-differencing-agg.test.js ./test/aggregations-test/significant-terms-agg.test.js ./test/aggregations-test/stats-agg.test.js ./test/aggregations-test/stats-bucket-agg.test.js ./test/aggregations-test/sum-agg.test.js ./test/aggregations-test/sum-bucket-agg.test.js ./test/aggregations-test/terms-agg-base.test.js ./test/aggregations-test/terms-agg.test.js ./test/aggregations-test/top-hits-agg.test.js ./test/aggregations-test/value-count-agg.test.js ./test/core-test/aggregation.test.js ./test/core-test/geo-point.test.js ./test/core-test/geo-shape.test.js ./test/core-test/highlight.test.js ./test/core-test/indexed-shape.test.js ./test/core-test/inner-hits.test.js ./test/core-test/query.test.js ./test/core-test/request-body-search.test.js ./test/core-test/rescore.test.js ./test/core-test/script.test.js ./test/core-test/sort.test.js ./test/core-test/util.test.js ./test/index.test.js ./test/queries-test/bool-query.test.js ./test/queries-test/boosting-query.test.js ./test/queries-test/common-terms-query.test.js ./test/queries-test/constant-score-query.test.js ./test/queries-test/decay-score-func.test.js ./test/queries-test/dis-max-query.test.js ./test/queries-test/exists-query.test.js ./test/queries-test/field-value-factor-func.test.js ./test/queries-test/full-text-query-base.test.js ./test/queries-test/function-score-query.test.js ./test/queries-test/fuzzy-query.test.js ./test/queries-test/geo-bounding-box-query.test.js ./test/queries-test/geo-distance-query.test.js ./test/queries-test/geo-polygon-query.test.js ./test/queries-test/geo-query-base.test.js ./test/queries-test/geo-shape-query.test.js ./test/queries-test/has-child-query.test.js ./test/queries-test/has-parent-query.test.js ./test/queries-test/ids-query.test.js ./test/queries-test/joining-query-base.test.js ./test/queries-test/match-all-query.test.js ./test/queries-test/match-none-query.test.js ./test/queries-test/match-phrase-prefix-query.test.js ./test/queries-test/match-phrase-query-base.test.js ./test/queries-test/match-phrase-query.test.js ./test/queries-test/match-query.test.js ./test/queries-test/mono-field-query-base.test.js ./test/queries-test/more-like-this-query.test.js ./test/queries-test/multi-match-query.test.js ./test/queries-test/nested-query.test.js ./test/queries-test/parent-id-query.test.js ./test/queries-test/percolate-query.test.js ./test/queries-test/prefix-query.test.js ./test/queries-test/query-string-query-base.test.js ./test/queries-test/query-string-query.test.js ./test/queries-test/random-score-func.test.js ./test/queries-test/range-query.test.js ./test/queries-test/regexp-query.test.js ./test/queries-test/score-func.test.js ./test/queries-test/script-query.test.js ./test/queries-test/script-score-func.test.js ./test/queries-test/simple-query-string-query.test.js ./test/queries-test/span-containing-query.test.js ./test/queries-test/span-field-masking-query.test.js ./test/queries-test/span-first-query.test.js ./test/queries-test/span-little-big-query-base.test.js ./test/queries-test/span-multi-term-query.test.js ./test/queries-test/span-near-query.test.js ./test/queries-test/span-not-query.test.js ./test/queries-test/span-or-query.test.js ./test/queries-test/span-term-query.test.js ./test/queries-test/span-within-query.test.js ./test/queries-test/term-query.test.js ./test/queries-test/terms-query.test.js ./test/queries-test/type-query.test.js ./test/queries-test/weight-score-func.test.js ./test/queries-test/wildcard-query.test.js ./test/recipes.test.js 1.5 kB {0} [built]
    + 278 hidden modules
Webpack output(multiple entries)
Hash: 40ba01d7a31287b335bc
Version: webpack 2.5.1
Time: 10753ms
                             Asset     Size    Chunks                    Chunk Names
   serial-differencing-agg.test.js  3.29 MB   62, 123  [emitted]  [big]  serial-differencing-agg.test
        common-terms-query.test.js  3.29 MB    0, 123  [emitted]  [big]  common-terms-query.test
                 geo-point.test.js  3.29 MB    2, 123  [emitted]  [big]  geo-point.test
               filters-agg.test.js  3.29 MB    3, 123  [emitted]  [big]  filters-agg.test
                   recipes.test.js   3.3 MB    4, 123  [emitted]  [big]  recipes.test
            wildcard-query.test.js  3.29 MB    5, 123  [emitted]  [big]  wildcard-query.test
               terms-query.test.js  3.29 MB    6, 123  [emitted]  [big]  terms-query.test
             span-or-query.test.js  3.29 MB    7, 123  [emitted]  [big]  span-or-query.test
            span-not-query.test.js  3.29 MB    8, 123  [emitted]  [big]  span-not-query.test
           span-near-query.test.js  3.29 MB    9, 123  [emitted]  [big]  span-near-query.test
     span-multi-term-query.test.js  3.29 MB   10, 123  [emitted]  [big]  span-multi-term-query.test
span-little-big-query-base.test.js  3.29 MB   11, 123  [emitted]  [big]  span-little-big-query-base.test
          span-first-query.test.js  3.29 MB   12, 123  [emitted]  [big]  span-first-query.test
  span-field-masking-query.test.js  3.29 MB   13, 123  [emitted]  [big]  span-field-masking-query.test
 simple-query-string-query.test.js  3.29 MB   14, 123  [emitted]  [big]  simple-query-string-query.test
         script-score-func.test.js  3.29 MB   15, 123  [emitted]  [big]  script-score-func.test
              script-query.test.js  3.29 MB   16, 123  [emitted]  [big]  script-query.test
                score-func.test.js  3.29 MB   17, 123  [emitted]  [big]  score-func.test
              regexp-query.test.js  3.29 MB   18, 123  [emitted]  [big]  regexp-query.test
               range-query.test.js  3.29 MB   19, 123  [emitted]  [big]  range-query.test
         random-score-func.test.js  3.29 MB   20, 123  [emitted]  [big]  random-score-func.test
        query-string-query.test.js  3.29 MB   21, 123  [emitted]  [big]  query-string-query.test
              prefix-query.test.js  3.29 MB   22, 123  [emitted]  [big]  prefix-query.test
           percolate-query.test.js  3.29 MB   23, 123  [emitted]  [big]  percolate-query.test
           parent-id-query.test.js  3.29 MB   24, 123  [emitted]  [big]  parent-id-query.test
              nested-query.test.js  3.29 MB   25, 123  [emitted]  [big]  nested-query.test
         multi-match-query.test.js  3.29 MB   26, 123  [emitted]  [big]  multi-match-query.test
      more-like-this-query.test.js  3.29 MB   27, 123  [emitted]  [big]  more-like-this-query.test
               match-query.test.js  3.29 MB   28, 123  [emitted]  [big]  match-query.test
 match-phrase-prefix-query.test.js  3.29 MB   29, 123  [emitted]  [big]  match-phrase-prefix-query.test
        joining-query-base.test.js  3.29 MB   30, 123  [emitted]  [big]  joining-query-base.test
                 ids-query.test.js  3.29 MB   31, 123  [emitted]  [big]  ids-query.test
          has-parent-query.test.js  3.29 MB   32, 123  [emitted]  [big]  has-parent-query.test
           has-child-query.test.js  3.29 MB   33, 123  [emitted]  [big]  has-child-query.test
           geo-shape-query.test.js  3.29 MB   34, 123  [emitted]  [big]  geo-shape-query.test
         geo-polygon-query.test.js  3.29 MB   35, 123  [emitted]  [big]  geo-polygon-query.test
        geo-distance-query.test.js  3.29 MB   36, 123  [emitted]  [big]  geo-distance-query.test
    geo-bounding-box-query.test.js  3.29 MB   37, 123  [emitted]  [big]  geo-bounding-box-query.test
               fuzzy-query.test.js  3.29 MB   38, 123  [emitted]  [big]  fuzzy-query.test
      function-score-query.test.js  3.29 MB   39, 123  [emitted]  [big]  function-score-query.test
   field-value-factor-func.test.js  3.29 MB   40, 123  [emitted]  [big]  field-value-factor-func.test
             dis-max-query.test.js  3.29 MB   41, 123  [emitted]  [big]  dis-max-query.test
          decay-score-func.test.js  3.29 MB   42, 123  [emitted]  [big]  decay-score-func.test
      constant-score-query.test.js  3.29 MB   43, 123  [emitted]  [big]  constant-score-query.test
            boosting-query.test.js  3.29 MB   44, 123  [emitted]  [big]  boosting-query.test
                bool-query.test.js  3.29 MB   45, 123  [emitted]  [big]  bool-query.test
                      sort.test.js  3.29 MB   46, 123  [emitted]  [big]  sort.test
                   rescore.test.js  3.29 MB   47, 123  [emitted]  [big]  rescore.test
       request-body-search.test.js   3.3 MB   48, 123  [emitted]  [big]  request-body-search.test
                inner-hits.test.js  3.29 MB   49, 123  [emitted]  [big]  inner-hits.test
             indexed-shape.test.js  3.29 MB   50, 123  [emitted]  [big]  indexed-shape.test
                 highlight.test.js   3.3 MB   51, 123  [emitted]  [big]  highlight.test
                 geo-shape.test.js  3.29 MB   52, 123  [emitted]  [big]  geo-shape.test
               aggregation.test.js  3.29 MB   53, 123  [emitted]  [big]  aggregation.test
           value-count-agg.test.js  3.29 MB   54, 123  [emitted]  [big]  value-count-agg.test
              top-hits-agg.test.js  3.29 MB   55, 123  [emitted]  [big]  top-hits-agg.test
                 terms-agg.test.js  3.29 MB   56, 123  [emitted]  [big]  terms-agg.test
            sum-bucket-agg.test.js  3.29 MB   57, 123  [emitted]  [big]  sum-bucket-agg.test
                   sum-agg.test.js  3.29 MB   58, 123  [emitted]  [big]  sum-agg.test
          stats-bucket-agg.test.js  3.29 MB   59, 123  [emitted]  [big]  stats-bucket-agg.test
                 stats-agg.test.js  3.29 MB   60, 123  [emitted]  [big]  stats-agg.test
     significant-terms-agg.test.js  3.29 MB   61, 123  [emitted]  [big]  significant-terms-agg.test
                    script.test.js  3.29 MB    1, 123  [emitted]  [big]  script.test
       scripted-metric-agg.test.js  3.29 MB   63, 123  [emitted]  [big]  scripted-metric-agg.test
               sampler-agg.test.js  3.29 MB   64, 123  [emitted]  [big]  sampler-agg.test
        reverse-nested-agg.test.js  3.29 MB   65, 123  [emitted]  [big]  reverse-nested-agg.test
    percentiles-bucket-agg.test.js  3.29 MB   66, 123  [emitted]  [big]  percentiles-bucket-agg.test
           percentiles-agg.test.js  3.29 MB   67, 123  [emitted]  [big]  percentiles-agg.test
      percentile-ranks-agg.test.js  3.29 MB   68, 123  [emitted]  [big]  percentile-ranks-agg.test
                nested-agg.test.js  3.29 MB   69, 123  [emitted]  [big]  nested-agg.test
        moving-average-agg.test.js  3.29 MB   70, 123  [emitted]  [big]  moving-average-agg.test
               missing-agg.test.js  3.29 MB   71, 123  [emitted]  [big]  missing-agg.test
            min-bucket-agg.test.js  3.29 MB   72, 123  [emitted]  [big]  min-bucket-agg.test
                   min-agg.test.js  3.29 MB   73, 123  [emitted]  [big]  min-agg.test
          metrics-agg-base.test.js  3.29 MB   74, 123  [emitted]  [big]  metrics-agg-base.test
            max-bucket-agg.test.js  3.29 MB   75, 123  [emitted]  [big]  max-bucket-agg.test
                   max-agg.test.js  3.29 MB   76, 123  [emitted]  [big]  max-agg.test
          matrix-stats-agg.test.js  3.29 MB   77, 123  [emitted]  [big]  matrix-stats-agg.test
              ip-range-agg.test.js  3.29 MB   78, 123  [emitted]  [big]  ip-range-agg.test
             histogram-agg.test.js  3.29 MB   79, 123  [emitted]  [big]  histogram-agg.test
                global-agg.test.js  3.29 MB   80, 123  [emitted]  [big]  global-agg.test
         geo-hash-grid-agg.test.js  3.29 MB   81, 123  [emitted]  [big]  geo-hash-grid-agg.test
          geo-distance-agg.test.js  3.29 MB   82, 123  [emitted]  [big]  geo-distance-agg.test
          geo-centroid-agg.test.js  3.29 MB   83, 123  [emitted]  [big]  geo-centroid-agg.test
            geo-bounds-agg.test.js  3.29 MB   84, 123  [emitted]  [big]  geo-bounds-agg.test
                filter-agg.test.js  3.29 MB   85, 123  [emitted]  [big]  filter-agg.test
 extended-stats-bucket-agg.test.js  3.29 MB   86, 123  [emitted]  [big]  extended-stats-bucket-agg.test
        extended-stats-agg.test.js  3.29 MB   87, 123  [emitted]  [big]  extended-stats-agg.test
   diversified-sampler-agg.test.js  3.29 MB   88, 123  [emitted]  [big]  diversified-sampler-agg.test
            derivative-agg.test.js  3.29 MB   89, 123  [emitted]  [big]  derivative-agg.test
        date-histogram-agg.test.js  3.29 MB   90, 123  [emitted]  [big]  date-histogram-agg.test
        cumulative-sum-agg.test.js  3.29 MB   91, 123  [emitted]  [big]  cumulative-sum-agg.test
              children-agg.test.js  3.29 MB   92, 123  [emitted]  [big]  children-agg.test
           cardinality-agg.test.js  3.29 MB   93, 123  [emitted]  [big]  cardinality-agg.test
       bucket-selector-agg.test.js  3.29 MB   94, 123  [emitted]  [big]  bucket-selector-agg.test
         bucket-script-agg.test.js  3.29 MB   95, 123  [emitted]  [big]  bucket-script-agg.test
           bucket-agg-base.test.js  3.29 MB   96, 123  [emitted]  [big]  bucket-agg-base.test
            avg-bucket-agg.test.js  3.29 MB   97, 123  [emitted]  [big]  avg-bucket-agg.test
                   avg-agg.test.js  3.29 MB   98, 123  [emitted]  [big]  avg-agg.test
         weight-score-func.test.js  3.27 MB        99  [emitted]  [big]  weight-score-func.test
                type-query.test.js  3.27 MB       100  [emitted]  [big]  type-query.test
                term-query.test.js  3.27 MB       101  [emitted]  [big]  term-query.test
         span-within-query.test.js  3.27 MB       102  [emitted]  [big]  span-within-query.test
           span-term-query.test.js  3.27 MB       103  [emitted]  [big]  span-term-query.test
     span-containing-query.test.js  3.27 MB       104  [emitted]  [big]  span-containing-query.test
        match-phrase-query.test.js  3.27 MB       105  [emitted]  [big]  match-phrase-query.test
          match-none-query.test.js  3.27 MB       106  [emitted]  [big]  match-none-query.test
           match-all-query.test.js  3.27 MB       107  [emitted]  [big]  match-all-query.test
              exists-query.test.js  3.27 MB       108  [emitted]  [big]  exists-query.test
                     index.test.js  3.31 MB       109  [emitted]  [big]  index.test
                 range-agg.test.js  3.27 MB       110  [emitted]  [big]  range-agg.test
            date-range-agg.test.js  3.27 MB       111  [emitted]  [big]  date-range-agg.test
            terms-agg-base.test.js  1.21 MB  112, 123  [emitted]  [big]  terms-agg-base.test
            range-agg-base.test.js  1.22 MB  113, 123  [emitted]  [big]  range-agg-base.test
        histogram-agg-base.test.js  1.22 MB  114, 123  [emitted]  [big]  histogram-agg-base.test
         pipeline-agg-base.test.js   883 kB  115, 123  [emitted]  [big]  pipeline-agg-base.test
   query-string-query-base.test.js  1.07 MB  116, 123  [emitted]  [big]  query-string-query-base.test
   match-phrase-query-base.test.js  1.07 MB  117, 123  [emitted]  [big]  match-phrase-query-base.test
      full-text-query-base.test.js  1.07 MB  118, 123  [emitted]  [big]  full-text-query-base.test
     mono-field-query-base.test.js  1.05 MB       119  [emitted]  [big]  mono-field-query-base.test
            geo-query-base.test.js   821 kB  120, 123  [emitted]  [big]  geo-query-base.test
                     query.test.js   687 kB  121, 123  [emitted]  [big]  query.test
                      util.test.js   665 kB       122  [emitted]  [big]  util.test
                        _macros.js  71.8 kB       123  [emitted]         _macros
 [181] ./test/aggregations-test/extended-stats-agg.test.js 807 bytes {87} [built]
 [244] ./test/queries-test/fuzzy-query.test.js 529 bytes {38} [built]
 [245] ./test/queries-test/geo-bounding-box-query.test.js 1.44 kB {37} [built]
 [246] ./test/queries-test/geo-distance-query.test.js 1.07 kB {36} [built]
 [247] ./test/queries-test/geo-polygon-query.test.js 508 bytes {35} [built]
 [248] ./test/queries-test/geo-query-base.test.js 891 bytes {120} [built]
 [249] ./test/queries-test/geo-shape-query.test.js 1.19 kB {34} [built]
 [283] ./test/queries-test/span-or-query.test.js 685 bytes {7} [built]
 [284] ./test/queries-test/span-term-query.test.js 780 bytes {103} [built]
 [285] ./test/queries-test/span-within-query.test.js 245 bytes {102} [built]
 [286] ./test/queries-test/term-query.test.js 693 bytes {101} [built]
 [287] ./test/queries-test/terms-query.test.js 1.97 kB {6} [built]
 [288] ./test/queries-test/type-query.test.js 419 bytes {100} [built]
 [289] ./test/queries-test/weight-score-func.test.js 346 bytes {99} [built]
 [291] ./test/recipes.test.js 8.62 kB {4} [built]
    + 277 hidden modules

@novemberborn
Copy link
Member

The 1st/second run difference implies it might be because AVA precompiles the test file, which is much bigger with the webpack build. If you only run one file (no concurrency) you only pay that cost once, but with multiple files you end up paying it for each file.

@sudo-suhas
Copy link
Contributor Author

@novemberborn I am a little confused. What do you mean by AVA precompiles the test file? Isn't that what we are doing before feeding it to AVA?

@novemberborn
Copy link
Member

@sudo-suhas it looks like you're precompiling and bundling the source files. You then run the resulting bundle with AVA, which treats the entire bundle as a test file. Unless you disable it (which may not actually be entirely possible, can't recall at the moment) it'll then transpile the test file. See https://github.com/avajs/ava/blob/master/docs/recipes/babelrc.md for more.

@sudo-suhas
Copy link
Contributor Author

sudo-suhas commented May 30, 2017

From the README

We use our own bundled Babel with our @ava/stage-4 preset, as well as custom transforms for test and helper files.

Note that AVA will always apply a few internal plugins regardless of configuration, but they should not impact the behavior of your code.

I don't think there is a way to tell AVA that there is no need to transpile again even if we were to apply those transforms in out precompile step. ~17m is not acceptable even though it'll be once.

Are there any other options we can consider?

@novemberborn
Copy link
Member

@sudo-suhas for your use case I suppose it's better to build everything into one file. Not sure where that leaves us with this recipe though.

@danny-andrews
Copy link
Contributor

danny-andrews commented May 30, 2017

Maybe we should have this recipe recommend building everything to one file, but add a note explaining that all tests will be run in the same process. Also, this use-case makes me think that there should be an option to opt-out of ava's auto test transformation. @sindresorhus what are your thoughts on this?

@sudo-suhas
Copy link
Contributor Author

I have a solution for this but it is not pretty. Let me know what you think. Comments inline.

// npm scripts
{
  "scripts": {
    // Use the babel cli to compile the src files but preserve file structure
    "precompile-src": "cross-env NODE_ENV=test babel src --out-dir _src",
    // We still need to compile tests to change the require path for src files
    "precompile-tests": "cross-env NODE_ENV=test webpack --config webpack.config.test.js",
    "pretest": "npm run precompile-src && npm run precompile-tests",
    "test": "cross-env NODE_ENV=test nyc --cache ava _build --concurrency 3"
  }
}

Webpack Externals Docs - https://webpack.js.org/configuration/externals/#function

webpack.config.test.js

'use strict';

const path = require('path');
const glob = require('glob');

const nodeExternals = require('webpack-node-externals');

const testFiles = glob.sync('./test/**/*.js');

const entryObj = {};
for (let idx = 0; idx < testFiles.length; idx++) {
    const file = testFiles[idx];
    entryObj[path.basename(file, path.extname(file))] = file;
}

module.exports = {
    target: 'node',
    entry: entryObj,
    output: {
        path: path.resolve(__dirname, '_build'),
        filename: '[name].js'
    },
    externals: [
        nodeExternals(),
        // Rewrite the require paths to use _src
        (context, request, callback) => {
            // This is a little messy because tests are not output in original file structure
            // test/index.test.js -> _build/index.test.js 
            // => ../src -> ../_src
            // test/aggregations-test/avg-agg.test.js -> _build/avg-agg.test.js 
            // => ../../src -> ../_src
            if (request.includes('/src')) {
                const requestReqwrite = request
                    .replace('/src', '/_src')
                    .replace('../../_src', '../_src');
                return callback(null, `commonjs ${requestReqwrite}`);
            }
            callback();
        }
    ]
};
Webpack output
> cross-env NODE_ENV=test webpack --config webpack.config.test.js

Hash: 97f887db4e4364259826
Version: webpack 2.5.1
Time: 2646ms
                             Asset     Size    Chunks             Chunk Names
              top-hits-agg.test.js  29.3 kB   62, 108  [emitted]  top-hits-agg.test
span-little-big-query-base.test.js  26.3 kB    0, 108  [emitted]  span-little-big-query-base.test
        joining-query-base.test.js  27.1 kB    2, 108  [emitted]  joining-query-base.test
        common-terms-query.test.js  28.1 kB    3, 108  [emitted]  common-terms-query.test
                    script.test.js    28 kB    4, 108  [emitted]  script.test
                 geo-point.test.js  28.5 kB    5, 108  [emitted]  geo-point.test
               aggregation.test.js    29 kB    6, 108  [emitted]  aggregation.test
          metrics-agg-base.test.js    27 kB    7, 108  [emitted]  metrics-agg-base.test
               filters-agg.test.js  29.7 kB    8, 108  [emitted]  filters-agg.test
           bucket-agg-base.test.js  26.8 kB    9, 108  [emitted]  bucket-agg-base.test
                   recipes.test.js  32.9 kB   10, 108  [emitted]  recipes.test
            wildcard-query.test.js  25.9 kB   11, 108  [emitted]  wildcard-query.test
               terms-query.test.js  27.7 kB   12, 108  [emitted]  terms-query.test
             span-or-query.test.js    26 kB   13, 108  [emitted]  span-or-query.test
            span-not-query.test.js  26.2 kB   14, 108  [emitted]  span-not-query.test
           span-near-query.test.js  26.2 kB   15, 108  [emitted]  span-near-query.test
     span-multi-term-query.test.js    26 kB   16, 108  [emitted]  span-multi-term-query.test
          span-first-query.test.js  26.2 kB   17, 108  [emitted]  span-first-query.test
  span-field-masking-query.test.js  26.1 kB   18, 108  [emitted]  span-field-masking-query.test
 simple-query-string-query.test.js  25.5 kB   19, 108  [emitted]  simple-query-string-query.test
         script-score-func.test.js  26.5 kB   20, 108  [emitted]  script-score-func.test
              script-query.test.js  26.1 kB   21, 108  [emitted]  script-query.test
              regexp-query.test.js    26 kB   22, 108  [emitted]  regexp-query.test
               range-query.test.js  26.6 kB   23, 108  [emitted]  range-query.test
         random-score-func.test.js  25.4 kB   24, 108  [emitted]  random-score-func.test
        query-string-query.test.js  27.5 kB   25, 108  [emitted]  query-string-query.test
   query-string-query-base.test.js  26.9 kB   26, 108  [emitted]  query-string-query-base.test
              prefix-query.test.js  25.8 kB   27, 108  [emitted]  prefix-query.test
           percolate-query.test.js  26.5 kB   28, 108  [emitted]  percolate-query.test
           parent-id-query.test.js  26.1 kB   29, 108  [emitted]  parent-id-query.test
              nested-query.test.js    26 kB   30, 108  [emitted]  nested-query.test
         multi-match-query.test.js  28.8 kB   31, 108  [emitted]  multi-match-query.test
      more-like-this-query.test.js  29.4 kB   32, 108  [emitted]  more-like-this-query.test
               match-query.test.js  27.1 kB   33, 108  [emitted]  match-query.test
   match-phrase-query-base.test.js  25.9 kB   34, 108  [emitted]  match-phrase-query-base.test
 match-phrase-prefix-query.test.js  25.5 kB   35, 108  [emitted]  match-phrase-prefix-query.test
                 ids-query.test.js  26.2 kB   36, 108  [emitted]  ids-query.test
          has-parent-query.test.js  26.4 kB   37, 108  [emitted]  has-parent-query.test
           has-child-query.test.js  26.3 kB   38, 108  [emitted]  has-child-query.test
           geo-shape-query.test.js  26.9 kB   39, 108  [emitted]  geo-shape-query.test
            geo-query-base.test.js  26.3 kB   40, 108  [emitted]  geo-query-base.test
         geo-polygon-query.test.js  25.7 kB   41, 108  [emitted]  geo-polygon-query.test
        geo-distance-query.test.js  26.4 kB   42, 108  [emitted]  geo-distance-query.test
    geo-bounding-box-query.test.js  27.4 kB   43, 108  [emitted]  geo-bounding-box-query.test
               fuzzy-query.test.js  25.8 kB   44, 108  [emitted]  fuzzy-query.test
      function-score-query.test.js  27.8 kB   45, 108  [emitted]  function-score-query.test
      full-text-query-base.test.js  26.2 kB   46, 108  [emitted]  full-text-query-base.test
   field-value-factor-func.test.js  26.2 kB   47, 108  [emitted]  field-value-factor-func.test
             dis-max-query.test.js  26.4 kB   48, 108  [emitted]  dis-max-query.test
          decay-score-func.test.js  27.2 kB   49, 108  [emitted]  decay-score-func.test
      constant-score-query.test.js  26.5 kB   50, 108  [emitted]  constant-score-query.test
            boosting-query.test.js  26.9 kB   51, 108  [emitted]  boosting-query.test
                bool-query.test.js  28.4 kB   52, 108  [emitted]  bool-query.test
                      sort.test.js  28.9 kB   53, 108  [emitted]  sort.test
                   rescore.test.js    27 kB   54, 108  [emitted]  rescore.test
       request-body-search.test.js  35.6 kB   55, 108  [emitted]  request-body-search.test
                     query.test.js  25.9 kB   56, 108  [emitted]  query.test
                inner-hits.test.js  28.4 kB   57, 108  [emitted]  inner-hits.test
             indexed-shape.test.js  25.9 kB   58, 108  [emitted]  indexed-shape.test
                 highlight.test.js  35.4 kB   59, 108  [emitted]  highlight.test
                 geo-shape.test.js  27.6 kB   60, 108  [emitted]  geo-shape.test
           value-count-agg.test.js  25.8 kB   61, 108  [emitted]  value-count-agg.test
                score-func.test.js  26.4 kB    1, 108  [emitted]  score-func.test
                 terms-agg.test.js  28.2 kB   63, 108  [emitted]  terms-agg.test
            terms-agg-base.test.js  26.5 kB   64, 108  [emitted]  terms-agg-base.test
            sum-bucket-agg.test.js  25.8 kB   65, 108  [emitted]  sum-bucket-agg.test
                   sum-agg.test.js  25.6 kB   66, 108  [emitted]  sum-agg.test
          stats-bucket-agg.test.js  25.8 kB   67, 108  [emitted]  stats-bucket-agg.test
                 stats-agg.test.js  25.6 kB   68, 108  [emitted]  stats-agg.test
     significant-terms-agg.test.js  27.4 kB   69, 108  [emitted]  significant-terms-agg.test
   serial-differencing-agg.test.js    26 kB   70, 108  [emitted]  serial-differencing-agg.test
       scripted-metric-agg.test.js    27 kB   71, 108  [emitted]  scripted-metric-agg.test
               sampler-agg.test.js    26 kB   72, 108  [emitted]  sampler-agg.test
        reverse-nested-agg.test.js  26.4 kB   73, 108  [emitted]  reverse-nested-agg.test
            range-agg-base.test.js  27.7 kB   74, 108  [emitted]  range-agg-base.test
         pipeline-agg-base.test.js  26.4 kB   75, 108  [emitted]  pipeline-agg-base.test
    percentiles-bucket-agg.test.js  26.2 kB   76, 108  [emitted]  percentiles-bucket-agg.test
           percentiles-agg.test.js  26.8 kB   77, 108  [emitted]  percentiles-agg.test
      percentile-ranks-agg.test.js  27.1 kB   78, 108  [emitted]  percentile-ranks-agg.test
                nested-agg.test.js  26.3 kB   79, 108  [emitted]  nested-agg.test
        moving-average-agg.test.js  26.7 kB   80, 108  [emitted]  moving-average-agg.test
               missing-agg.test.js  25.8 kB   81, 108  [emitted]  missing-agg.test
            min-bucket-agg.test.js  25.6 kB   82, 108  [emitted]  min-bucket-agg.test
                   min-agg.test.js  25.6 kB   83, 108  [emitted]  min-agg.test
            max-bucket-agg.test.js  25.6 kB   84, 108  [emitted]  max-bucket-agg.test
                   max-agg.test.js  25.6 kB   85, 108  [emitted]  max-agg.test
          matrix-stats-agg.test.js  26.5 kB   86, 108  [emitted]  matrix-stats-agg.test
              ip-range-agg.test.js  26.7 kB   87, 108  [emitted]  ip-range-agg.test
             histogram-agg.test.js  25.6 kB   88, 108  [emitted]  histogram-agg.test
        histogram-agg-base.test.js  28.5 kB   89, 108  [emitted]  histogram-agg-base.test
                global-agg.test.js  25.6 kB   90, 108  [emitted]  global-agg.test
         geo-hash-grid-agg.test.js  26.8 kB   91, 108  [emitted]  geo-hash-grid-agg.test
          geo-distance-agg.test.js  27.2 kB   92, 108  [emitted]  geo-distance-agg.test
          geo-centroid-agg.test.js  25.8 kB   93, 108  [emitted]  geo-centroid-agg.test
            geo-bounds-agg.test.js  26.3 kB   94, 108  [emitted]  geo-bounds-agg.test
                filter-agg.test.js  26.4 kB   95, 108  [emitted]  filter-agg.test
 extended-stats-bucket-agg.test.js  26.2 kB   96, 108  [emitted]  extended-stats-bucket-agg.test
        extended-stats-agg.test.js  26.1 kB   97, 108  [emitted]  extended-stats-agg.test
   diversified-sampler-agg.test.js  26.5 kB   98, 108  [emitted]  diversified-sampler-agg.test
            derivative-agg.test.js    26 kB   99, 108  [emitted]  derivative-agg.test
        date-histogram-agg.test.js  26.1 kB  100, 108  [emitted]  date-histogram-agg.test
        cumulative-sum-agg.test.js  25.8 kB  101, 108  [emitted]  cumulative-sum-agg.test
              children-agg.test.js  26.1 kB  102, 108  [emitted]  children-agg.test
           cardinality-agg.test.js  26.3 kB  103, 108  [emitted]  cardinality-agg.test
       bucket-selector-agg.test.js  26.6 kB  104, 108  [emitted]  bucket-selector-agg.test
         bucket-script-agg.test.js  26.4 kB  105, 108  [emitted]  bucket-script-agg.test
            avg-bucket-agg.test.js  25.7 kB  106, 108  [emitted]  avg-bucket-agg.test
                   avg-agg.test.js  25.6 kB  107, 108  [emitted]  avg-agg.test
                        _macros.js    24 kB       108  [emitted]  _macros
         weight-score-func.test.js  3.77 kB       109  [emitted]  weight-score-func.test
                type-query.test.js  3.89 kB       110  [emitted]  type-query.test
                term-query.test.js  4.12 kB       111  [emitted]  term-query.test
         span-within-query.test.js  3.63 kB       112  [emitted]  span-within-query.test
           span-term-query.test.js  4.32 kB       113  [emitted]  span-term-query.test
     span-containing-query.test.js  3.64 kB       114  [emitted]  span-containing-query.test
     mono-field-query-base.test.js  4.25 kB       115  [emitted]  mono-field-query-base.test
        match-phrase-query.test.js  3.71 kB       116  [emitted]  match-phrase-query.test
          match-none-query.test.js  3.62 kB       117  [emitted]  match-none-query.test
           match-all-query.test.js  3.62 kB       118  [emitted]  match-all-query.test
              exists-query.test.js   3.8 kB       119  [emitted]  exists-query.test
                     index.test.js  46.3 kB       120  [emitted]  index.test
                      util.test.js  3.66 kB       121  [emitted]  util.test
                 range-agg.test.js   3.8 kB       122  [emitted]  range-agg.test
            date-range-agg.test.js  4.54 kB       123  [emitted]  date-range-agg.test
   [2] ./test/_macros.js 21 kB {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13} {14} {15} {16} {17} {18} {19} {20} {21} {22} {23} {24} {25} {26} {27} {28} {29} {30} {31} {32} {33} {34} {35} {36} {37} {38} {39} {40} {41} {42} {43} {44} {45} {46} {47} {48} {49} {50} {51} {52} {53} {54} {55} {56} {57} {58} {59} {60} {61} {62} {63} {64} {65} {66} {67} {68} {69} {70} {71} {72} {73} {74} {75} {76} {77} {78} {79} {80} {81} {82} {83} {84} {85} {86} {87} {88} {89} {90} {91} {92} {93} {94} {95} {96} {97} {98} {99} {100} {101} {102} {103} {104} {105} {106} {107} {108} [built]
  [70] ./test/core-test/geo-point.test.js 2.3 kB {5} [built]
 [127] ./test/queries-test/span-little-big-query-base.test.js 655 bytes {0} [built]
 [128] ./test/queries-test/span-multi-term-query.test.js 698 bytes {16} [built]
 [129] ./test/queries-test/span-near-query.test.js 783 bytes {15} [built]
 [130] ./test/queries-test/span-not-query.test.js 680 bytes {14} [built]
 [131] ./test/queries-test/span-or-query.test.js 685 bytes {13} [built]
 [132] ./test/queries-test/span-term-query.test.js 780 bytes {113} [built]
 [133] ./test/queries-test/span-within-query.test.js 245 bytes {112} [built]
 [134] ./test/queries-test/term-query.test.js 693 bytes {111} [built]
 [135] ./test/queries-test/terms-query.test.js 1.97 kB {12} [built]
 [136] ./test/queries-test/type-query.test.js 419 bytes {110} [built]
 [137] ./test/queries-test/weight-score-func.test.js 346 bytes {109} [built]
 [138] ./test/queries-test/wildcard-query.test.js 664 bytes {11} [built]
 [139] ./test/recipes.test.js 8.62 kB {10} [built]
    + 125 hidden modules
Compiled test file sample
Original test file
import test from 'ava';
import { AvgAggregation } from '../../src';
import { setsAggType } from '../_macros';

test(setsAggType, AvgAggregation, 'avg');

test('constructor sets field', t => {
    const value = new AvgAggregation('my_agg', 'my_field').toJSON();
    const expected = {
        my_agg: {
            avg: {
                field: 'my_field'
            }
        }
    };
    t.deepEqual(value, expected);
});
Compiled file
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // identity function for calling harmony imports with the correct context
/******/    __webpack_require__.i = function(value) { return value; };
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, {
/******/                configurable: false,
/******/                enumerable: true,
/******/                get: getter
/******/            });
/******/        }
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __webpack_require__.n = function(module) {
/******/        var getter = module && module.__esModule ?
/******/            function getDefault() { return module['default']; } :
/******/            function getModuleExports() { return module; };
/******/        __webpack_require__.d(getter, 'a', getter);
/******/        return getter;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = 15);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

module.exports = require("ava");

/***/ }),
/* 1 */
/***/ (function(module, exports) {

module.exports = require("../_src");

/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["setsAggType"] = setsAggType;
/* harmony export (immutable) */ __webpack_exports__["illegalCall"] = illegalCall;
/* harmony export (immutable) */ __webpack_exports__["illegalParamType"] = illegalParamType;
/* harmony export (immutable) */ __webpack_exports__["validatedCorrectly"] = validatedCorrectly;
/* harmony export (immutable) */ __webpack_exports__["simpleExpect"] = simpleExpect;
/* harmony export (immutable) */ __webpack_exports__["aggsExpectStrategy"] = aggsExpectStrategy;
/* harmony export (immutable) */ __webpack_exports__["nameExpectStrategy"] = nameExpectStrategy;
/* harmony export (immutable) */ __webpack_exports__["nameFieldExpectStrategy"] = nameFieldExpectStrategy;
/* harmony export (immutable) */ __webpack_exports__["makeSetsOptionMacro"] = makeSetsOptionMacro;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_lodash__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_lodash___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_lodash__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__src_core_util__ = __webpack_require__(3);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__src_core_util___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__src_core_util__);



const ILLEGAL_PARAM = Object.create(null);

/**
 * Macro for testing that aggregation type is set as expected.
 *
 * @param {*} t
 * @param {function} Cls
 * @param {string} aggType
 * @param {Object=} defaultDef
 */
function setsAggType(t, Cls, aggType, defaultDef) {
    const value = new Cls('my_agg').toJSON();
    const expected = {
        my_agg: {
            [aggType]: Object.assign({}, defaultDef)
        }
    };
    t.deepEqual(value, expected);
}

setsAggType.title = (ignore, Cls, aggType) => `sets type as ${aggType}`;

/**
 * Macro for checking method cannot be called on the instance
 *
 * @param {*} t
 * @param {*} Cls constructor class
 * @param {string} propKey method name
 */
function illegalCall(t, Cls, propKey, ...args) {
    const err = t.throws(() => new Cls(...args)[propKey](), Error);
    t.is(err.message, `${propKey} is not supported in ${Cls.name}`);
}

illegalCall.title = (ignore, Cls, propKey) => `${__WEBPACK_IMPORTED_MODULE_0_lodash___default.a.snakeCase(propKey)} cannot be set`;

/**
 * Check that calling method on instance with illegal param type throws error
 *
 * @param {*} t
 * @param {*} instance
 * @param {string} method
 * @param {string} clsName
 */
function illegalParamType(t, instance, method, clsName) {
    let err = t.throws(() => instance[method](null), TypeError);
    t.is(err.message, `Argument must be an instance of ${clsName}`);

    err = t.throws(() => instance[method](ILLEGAL_PARAM), TypeError);
    t.is(err.message, `Argument must be an instance of ${clsName}`);
}

illegalParamType.title = (ignore, instance, method, clsName) => `checks ${clsName} class`;

/**
 * Macro for testing method validation
 *
 * @param {*} t
 * @param {function} getInstance
 * @param {string} method
 * @param {Array} validValues
 * @param {boolean=} toggleCase
 */
function validatedCorrectly(t, getInstance, method, validValues, toggleCase = true) {
    __WEBPACK_IMPORTED_MODULE_0_lodash___default.a.forEach(validValues, val => {
        t.notThrows(() => getInstance()[method](val));

        if (toggleCase) {
            t.notThrows(() => getInstance()[method](val.toLowerCase()));
            t.notThrows(() => getInstance()[method](val.toUpperCase()));
        }
    });

    t.throws(() => getInstance()[method](null));
    t.throws(() => getInstance()[method](`invalid_${__WEBPACK_IMPORTED_MODULE_0_lodash___default.a.snakeCase(method)}`));
}

validatedCorrectly.title = (ignore, getInstance, method) =>
    `${__WEBPACK_IMPORTED_MODULE_0_lodash___default.a.snakeCase(method)} correctly validated`;

/**
 * Simple strategy for checking option is set for use with `makeSetsOptionMacro`
 *
 * @param {string} keyName
 * @param {*} propValue
 * @returns {function}
 */
function simpleExpect(keyName, propValue) {
    return { [keyName]: propValue };
}

/**
 * Expect strategy for use with `makeSetsOptionMacro` for aggregations
 *
 * @param {string} name
 * @param {string} type
 * @param {Object} defaultDef
 * @returns {function}
 */
function aggsExpectStrategy(name, type, defaultDef) {
    return (keyName, propValue) => ({
        [name]: {
            [type]: Object.assign({ [keyName]: propValue }, defaultDef)
        }
    });
}

/**
 * Expect strategy for use with `makeSetsOptionMacro` for queries, score functions
 *
 * @param {string} name
 * @param {Object=} defaultDef
 * @returns {function}
 */
function nameExpectStrategy(name, defaultDef) {
    return (keyName, propValue) => ({
        [name]: Object.assign({}, defaultDef, { [keyName]: propValue })
    });
}

/**
 * Expect strategy for use with `makeSetsOptionMacro` for full text queries
 *
 * @param {string} name
 * @param {Object=} defaultDef
 * @returns {function}
 */
function nameFieldExpectStrategy(name, defaultDef) {
    return (keyName, propValue) => ({
        [name]: {
            my_field: Object.assign({}, defaultDef, { [keyName]: propValue })
        }
    });
}

/**
 * Make macro for checking property is set.
 *
 * @param {function} getInstance
 * @param {function=} getExpected Set to `simpleExpect` by default
 * @returns {function}
 */
function makeSetsOptionMacro(getInstance, getExpected = simpleExpect) {
    /**
     * Macro for testing that property is being set
     *
     * @param {*} t
     * @param {string} methodName
     * @param {Object} options
     * @param {*} options.param
     * @param {*=} options.propValue Optional argument for use when value passed is not the value set
     * @param {boolean=} options.spread If array is passed, to control spread
     * @param {string=} options.keyName Optional override argument, default is `_.snakeCase(methodName)`
     */
    function setsOption(
        t,
        methodName,
        { param, propValue = param, spread = true, keyName = __WEBPACK_IMPORTED_MODULE_0_lodash___default.a.snakeCase(methodName) }
    ) {
        const value = Array.isArray(param) && spread
            ? getInstance()[methodName](...param).toJSON()
            : getInstance()[methodName](param).toJSON();
        const expected = getExpected(keyName, __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__src_core_util__["recursiveToJSON"])(propValue));
        t.deepEqual(value, expected);
    }

    setsOption.title = (providedTitle, methodName) =>
        !__WEBPACK_IMPORTED_MODULE_0_lodash___default.a.isEmpty(providedTitle) ? providedTitle : `sets ${__WEBPACK_IMPORTED_MODULE_0_lodash___default.a.snakeCase(methodName)} option`;

    return setsOption;
}


/***/ }),
/* 3 */
/***/ (function(module, exports) {

module.exports = require("../_src/core/util");

/***/ }),
/* 4 */
/***/ (function(module, exports) {

module.exports = require("lodash");

/***/ }),
/* 5 */,
/* 6 */,
/* 7 */,
/* 8 */,
/* 9 */,
/* 10 */,
/* 11 */,
/* 12 */,
/* 13 */,
/* 14 */,
/* 15 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_ava__ = __webpack_require__(0);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_ava___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_ava__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__src__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__src___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__src__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__macros__ = __webpack_require__(2);




__WEBPACK_IMPORTED_MODULE_0_ava___default()(__WEBPACK_IMPORTED_MODULE_2__macros__["setsAggType"], __WEBPACK_IMPORTED_MODULE_1__src__["AvgAggregation"], 'avg');

__WEBPACK_IMPORTED_MODULE_0_ava___default()('constructor sets field', t => {
    const value = new __WEBPACK_IMPORTED_MODULE_1__src__["AvgAggregation"]('my_agg', 'my_field').toJSON();
    const expected = {
        my_agg: {
            avg: {
                field: 'my_field'
            }
        }
    };
    t.deepEqual(value, expected);
});


/***/ })
/******/ ]);

1st run time - 3m18.812s. Next run - 2m31.209s

Performance wise, single entry test file is the best.

Although this is too complicated to setup and introduces too many restrictions on file structure,
I think this could be integrated into AVA, enabled with an optional flag.
Essentially, AVA has to run the src files through babel to some other path,
maybe into the cache dir(?) and replace src with the new path.

@novemberborn
Copy link
Member

Maybe we should have this recipe recommend building everything to one file, but add a not explaining that all tests will be run in the same process.

@danny-andrews that makes sense. @sudo-suhas?

Also, this use-case makes me think that there should be an option to opt-out of ava's auto test transformation.

There's a plan for that. Eventually we may be able to integrate webpack in the same way.

@grvcoelho
Copy link

Maybe we should have this recipe recommend building everything to one file, but add a not explaining that all tests will be run in the same process.

@danny-andrews, @novemberborn: there is a huge downside on this approach: we lose the name of the tests outputted when in verbose mode. Because ava use the name and path of the file to output this on verbose mode.

@sudo-suhas
Copy link
Contributor Author

@danny-andrews that makes sense. @sudo-suhas?

@novemberborn Initially I did agree with the suggestion. However, I am not so sure now.

All the various approaches we have discussed come with their own but. However, this discussion itself can be very useful for somebody trying to setup precompilation. So why not add a paragraph summarising the discussion and linking back to this thread?

@danny-andrews
Copy link
Contributor

@sudo-suhas I agree. There is a lot of nuance surrounding this setup. It might be better to list the different approaches discussed here along with the pros and cons of each without explicitly recommending one over the other, as there doesn't seem to be one best solution as of yet. When this is completed, this may no longer be the case.

@novemberborn
Copy link
Member

@sudo-suhas sounds good 👍

@sudo-suhas
Copy link
Contributor Author

sudo-suhas commented Jun 4, 2017

I have updated the PR but it might be a bit too long. Let me know if you think we can drop any of the discussed approaches or trim anything. Markdown link

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 great @sudo-suhas, thank you!

I've left some comments on sentence structure and some typos, as well as some code questions. Excited to see this land 👍

@@ -2,25 +2,33 @@

Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/docs/recipes/precompiling-with-webpack.md)

The AVA [readme](https://github.com/avajs/ava#transpiling-imported-modules) mentions precompiling your imported modules as an alternative to runtime compilation, but it doesn't explain how. This recipe shows how to do this using webpack. (This example uses webpack 2.0)
The AVA [readme](https://github.com/avajs/ava#transpiling-imported-modules) mentions precompiling your imported modules as an alternative to runtime compilation, but it doesn't explain how. This recipe various approaches using webpack. (These examples use webpack 2.0). Multiple approaches are discussed as each has its own pros and cons. You should select the approach that best fits your use case. This might not be necessery once [this](https://github.com/avajs/ava/blob/master/docs/specs/001%20-%20Improving%20language%20support.md) is completed. See the original discussion [here](/avajs/ava/pull/1385).
Copy link
Member

Choose a reason for hiding this comment

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

Rather than:

This recipe various approaches using webpack. (These examples use webpack 2.0).

Perhaps:

This recipe discusses several approaches using webpack v2.

This seems distracting:

This might not be necessery once this is completed.

And the discussion URL should have https://github.com in front of it.

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 seems distracting:

Thought it would be better to acknowledge the difficulty of precompiling and mention that a better permanent solution is in the works. Would you prefer I reword it or remove it?

And the discussion URL should have https://github.com in front of it.

I thought it might be better to have relative links. And that / would go down to the root. I'll just change this to use full URL. There is also the option of using avajs/ava#1385. Let me know if you'd prefer it.

Copy link
Member

Choose a reason for hiding this comment

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

Thought it would be better to acknowledge the difficulty of precompiling and mention that a better permanent solution is in the works. Would you prefer I reword it or remove it?

I'd rather these recipes focus on what's possible now, rather than what might be possible eventually. I mean I wrote that RFC in January… it's not a quick fix 😉

I thought it might be better to have relative links. And that / would go down to the root. I'll just change this to use full URL. There is also the option of using #1385. Let me know if you'd prefer it.

Yea I know. I suppose I just don't trust linking within GitHub that much, plus it means the links won't work if the file is rendered outside of GitHub.

const nodeExternals = require('webpack-node-externals');

module.exports = {
entry: ['src/tests.js'],
entry: ['tests.js'],
Copy link
Member

Choose a reason for hiding this comment

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

This assumes you only have one test file (named tests.js)? Perhaps the file name should be test.js then? (At least that's how I name such a file, analogous to having a test directory.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Although there is a single file that's not the same thing as having a single test. I don't mind changing this though.

Copy link
Member

Choose a reason for hiding this comment

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

I hear ya. We do talk about test.js in the readme though.

Alternatively: your-test-file.js?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let's go with test.js. Sold on the readme.

- [Multiple entry files](#multiple-entry-files)
- [Multiple entry files with externalized source files](#multiple-entry-files-with-externalized-source-files)

#### Refer precompiled source in tests
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps Test against precompiled sources?

- [Multiple entry files with externalized source files](#multiple-entry-files-with-externalized-source-files)

#### Refer precompiled source in tests
Source files can be compiled with source maps(for coverage) to `_src` folder and referenced in tests. While this is less than elegant, it performs well and the worklow can be optimized with [webpack's watch mode](https://webpack.js.org/configuration/watch/).
Copy link
Member

Choose a reason for hiding this comment

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

Let's not bring code coverage into this discussion, since that's a whole other can of worms.

Why _src here versus _build above?

There's a typo in workflow (it's missing the l).

How would users do this precompilation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why _src here versus _build above?

I was thinking _build for tests and _src for source.

How would users do this precompilation?

I was thinking babel-cli.. I'll change this to mention babel-cli watch mode instead of webpack. Sound good?

Copy link
Member

Choose a reason for hiding this comment

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

I was thinking _build for tests and _src for source.

Sure. I'm probably overthinking this 😄

How would users do this precompilation?

I was thinking babel-cli.. I'll change this to mention babel-cli watch mode instead of webpack. Sound good?

👍

```

#### Single entry file
To pre-compile folder with multiple test files, we can use globbing(see [stackoverflow answer](http://stackoverflow.com/questions/32874025/how-to-add-wildcard-mapping-in-entry-of-webpack/34545812#34545812)). Although this performs quite possibly the best, it comes at a cost. AVA uses the filename and path for test names in verbose mode as well as test failure reports. This can make the error report harder to understand. Also, it can matter if tests modify globals, Node.js built-ins. You wouldn't want that to leak across tests.
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 putting it like this?

Multiple test files can be compiled into a single file. This may have the best performance but that does come at a cost. All tests will be in the same file, which can make it harder to know which test has failed, since AVA can't show the file name the test was originally in. You'll also lose process isolation.

for (let idx = 0; idx < len; idx++) {
const file = testFiles[idx];
entryObj[path.basename(file, path.extname(file))] = file;
}
Copy link
Member

Choose a reason for hiding this comment

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

This code can be improved a bit:

const testFiles = glob.sync('./test/**/*.js').reduce((entryObj, file) => {
  entryObj[path.basename(file, path.extname(file))] = file;
  return entryObj;
}, {});

Then use entry: testFiles below.

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 too used reduce initially but felt the for method was easier to understand. The reduce returns the entryObj and not the testFiles though. I'll change it to use reduce.

```

#### Multiple entry files with externalized source files
This is the most complicated to setup but performs quite well and also retains file names. In this approach, we use the babel cli to compile the src files but preserve file structure. Then we compile the tests with a require path rewrite. The following example is for a specific file structure. Depending on how your source and test files are organised, you might need to make changes.
Copy link
Member

Choose a reason for hiding this comment

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

I'd replace "babel cli" with babel-cli. Rather than "compile the src file" I'd use "compile the source files". And I'd invert the sentence structure from:

Then we compile the tests with a require path rewrite"

To:

Require paths in tests are rewritten when compiling them in webpack.

for (let idx = 0; idx < len; idx++) {
const file = testFiles[idx];
entryObj[path.basename(file, path.extname(file))] = file;
}
Copy link
Member

Choose a reason for hiding this comment

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

Same as above.

@sudo-suhas
Copy link
Contributor Author

@novemberborn Thanks for taking the time to review.

@novemberborn novemberborn merged commit f228401 into avajs:master Jun 17, 2017
@novemberborn
Copy link
Member

Landed. Thanks @sudo-suhas for your work on this, and thank you @danny-andrews and @grvcoelho for your contributions in the discussion.

@sudo-suhas sudo-suhas deleted the docs_precompile_multi branch June 18, 2017 00:14
kevva pushed a commit that referenced this pull request Sep 13, 2017
Discuss various options and trade-offs in how to use webpack builds as a way to run AVA tests.
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.

None yet

4 participants