Stack Traces and Line Numbers that Make Sense
We're getting a better and better setup for developing and deploying our applications. We can organize our JavaScript and CSS, we can use third party libraries for both, can deploy to production in a proper way, and run tests. But we're still missing something every other programming language has: stack traces.
Stack traces tell us where in our code errors are happening. In most programming languages, when an uncaught error happens, we see some information about what line of code raised the error, as well as the path through the code that led to that error. Even in a language like C, we can do this. Not so in JavaScript.
The reason is that the file we're editing is not the file that's being executed. Webpack is compiling all of our files together, as well as minifying them and including our third party libraries.
Let's see the problem in action.
Add a throw
to the function in markdownPreviewer.js
, after event.preventDefault()
.
Re-run Webpack and open up dev/index.html
, then open the JavaScript console, and then click the “Preview” button:
The error came from line 1 of our bundle. This is technically true, since our bundle is minified and all the code is on one line. Since we aren't editing this file directly, we have no idea where this error came from in our original source code.
The solution to this is a feature that most browsers support called sourcemaps.
Sourcemaps
If a sourcemap file exists, browsers know to look at it when giving you a stack trace (though you may need to enable this feature in your browser). The sourcemap lets the browser tell us
that the problem wasn't in line 1 of our bundle, but on line 10 of markdownPreviewer.js
.
Webpack can produce sourcemaps. The configuration option is, of course, not called something intuitive like sourceMap
, but instead is called devtool
.
The possible values for devtool
are many, and poorly documented. Since we have different configurations
for production and development, we can use different source map strategies. Let's try dev first as that's where we need the most
help.
The docs' first recommandation is eval
which, and I'm not making this up, is documented to not work at all:
This [
eval
] is pretty fast. The main disadvantage is that it doesn't display line numbers correctly
Like…why am I using source maps if I am also OK with the line numbers being wrong? That's the entire point of source
maps. Ugh. Unfortunately, the second choice, eval-source-map
works for JS and not for CSS. So, we'll go with inline-source-map
which, so far, works for both. Add it to config/webpack.dev.config.js
like so:
const path = require('path');
const Merge = require('webpack-merge');
const CommonConfig = require('./webpack.common.config.js');
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module.exports = Merge(CommonConfig, {
output: {
path: path.join(__dirname, '../dev'),
filename: 'bundle.js'
},
/* start new code */
devtool: "inline-source-map",
/* end new code */
plugins: [
new MiniCssExtractPlugin({ filename: "styles.css" })
]
});
Run Webpack:
> yarn webpack
yarn run v1.21.1
$ webpack $npm_package_config_webpack_args
Hash: 4f3ab66cec3145c1dcd4
Version: webpack 4.41.5
Time: 850ms
Built at: 01/21/2020 5:47:30 PM
Asset Size Chunks Chunk Names
bundle.js 210 KiB 0 [emitted] main
index.html 833 bytes [emitted]
styles.css 327 KiB 0 [emitted] main
Entrypoint main = styles.css bundle.js
[0] ./js/index.js 421 bytes {0} [built]
[2] ./css/styles.css 39 bytes {0} [built]
[3] ./js/markdownPreviewer.js 381 bytes {0} [built]
+ 9 hidden modules
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = index.html
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./html/index.html 961 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
+ 1 hidden module
Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!css/styles.css:
Entrypoint mini-css-extract-plugin = *
[0] ./node_modules/css-loader/dist/cjs.js!./css/styles.css 303 bytes {0} [built]
+ 1 hidden module
Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/tachyons/css/tachyons.css:
Entrypoint mini-css-extract-plugin = *
2 modules
Done in 1.48s.
And, if we follow the same steps in the browser, we'll see that we can now see the line number where our error is coming from!
If you are in Chrome and click the stack, it even shows you the code where the error came from!
Nice!
Unfortunately, we cannot enable source maps in production. While it is documented to work, it actually doesn't. The documentation provides for several different production-quality source map configurations and none of them produce usable source maps at this time.
The reason this is so bad is that in a real production application, we will have some level of error monitoring. We need to know if there are real errors in the front-end and where they are happening. Without source maps we cannot know that (especially when minifying).
This means that for production we either use a development-style source map (which increases our bundle size), or we don't get source maps at all. Welp.
What about our CSS? Sometimes it's nice to know where certain styles are defined or where they came from.
Sourcemaps for CSS
If you remove the configuration we just made, reload your app, inspect and element and examine the styles, you'll see they are
all defined somewhere in styles.css
. That is obviously not true. If you restore the use of inline-source-map
in dev, you
should see that the definition of styles is correctly mapped to where those styles were defined.
As with JS, it's not possible to get this to work in production mode while style hashing and minifying our CSS. This is less of an issue because CSS doesn't generate stack traces, but it still sucks that the tool documents a thing that doesn't actually work.
What about tests?
Stack Traces in Tests
If we introduce a failure into our tests, we'll see a stack trace, but the line number is useless, as before.
First, remove the throw
you added before from js/markdownPreviewer.js
. Next, let's introduce a test failure in our test.
Since our test isn't a real test, we'll replace the expectation that we've loaded our code with a nonsense test that undefined
is defined:
import markdownPreviewer from "../js/markdownPreviewer"
describe("markdownPreviewer", function() {
it("should exist", function() {
/* start new code */
expect(undefined).toBeDefined();
/* end new code */
});
});
> yarn test
yarn run v1.21.1
$ yarn webpack:test && yarn jest
$ webpack --config test/webpack.test.config.js --display-error-details
Hash: fa0080dd7d518bd83b2a
Version: webpack 4.41.5
Time: 431ms
Built at: 01/21/2020 5:47:33 PM
Asset Size Chunks Chunk Names
bundle.test.js 144 KiB 0 [emitted] main
Entrypoint main = bundle.test.js
[0] multi ./node_modules/gensync/test/index.test.js ./test/canary.test.js ./test/markdownPreviewer.test.js 52 bytes {0} [built]
[17] (webpack)/buildin/global.js 472 bytes {0} [built]
[52] ./test/canary.test.js 107 bytes {0} [built]
[53] ./test/markdownPreviewer.test.js 221 bytes {0} [built]
[54] ./js/markdownPreviewer.js 381 bytes {0} [built]
+ 52 hidden modules
$ jest test/bundle.test.js
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
299 | });
300 |
> 301 | describe("'sync' handler", async () => {
| ^
302 | test("success", async () => {
303 | const fn = gensync({
304 | sync: (...args) => JSON.stringify(args),
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/bundle.test.js:301:3)
at Object.call (test/bundle.test.js:162:1)
at Object.call (test/bundle.test.js:577:30)
at __webpack_require__ (test/bundle.test.js:20:30)
at Object.call (test/bundle.test.js:91:1)
at __webpack_require__ (test/bundle.test.js:20:30)
at test/bundle.test.js:84:18
at Object.<anonymous> (test/bundle.test.js:1:10)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
319 | });
320 |
> 321 | describe("'async' handler", async () => {
| ^
322 | test("success", async () => {
323 | const fn = gensync({
324 | sync: throwTestError,
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/bundle.test.js:321:3)
at Object.call (test/bundle.test.js:162:1)
at Object.call (test/bundle.test.js:577:30)
at __webpack_require__ (test/bundle.test.js:20:30)
at Object.call (test/bundle.test.js:91:1)
at __webpack_require__ (test/bundle.test.js:20:30)
at test/bundle.test.js:84:18
at Object.<anonymous> (test/bundle.test.js:1:10)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
339 | });
340 |
> 341 | describe("'errback' sync handler", async () => {
| ^
342 | test("success", async () => {
343 | const fn = gensync({
344 | sync: throwTestError,
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/bundle.test.js:341:3)
at Object.call (test/bundle.test.js:162:1)
at Object.call (test/bundle.test.js:577:30)
at __webpack_require__ (test/bundle.test.js:20:30)
at Object.call (test/bundle.test.js:91:1)
at __webpack_require__ (test/bundle.test.js:20:30)
at test/bundle.test.js:84:18
at Object.<anonymous> (test/bundle.test.js:1:10)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
359 | });
360 |
> 361 | describe("'errback' async handler", async () => {
| ^
362 | test("success", async () => {
363 | const fn = gensync({
364 | sync: throwTestError,
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/bundle.test.js:361:3)
at Object.call (test/bundle.test.js:162:1)
at Object.call (test/bundle.test.js:577:30)
at __webpack_require__ (test/bundle.test.js:20:30)
at Object.call (test/bundle.test.js:91:1)
at __webpack_require__ (test/bundle.test.js:20:30)
at test/bundle.test.js:84:18
at Object.<anonymous> (test/bundle.test.js:1:10)
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
FAIL test/bundle.test.js
gensync({})
option validation
✓ disallow async and errback handler together (5ms)
✓ disallow missing sync handler (1ms)
✓ errback callback required (1ms)
generator function metadata
✓ automatic naming (1ms)
✓ explicit naming
✓ default arity
✓ explicit arity (1ms)
'sync' handler
✓ success (7ms)
✓ failure (2ms)
'async' handler
✓ success (2ms)
✓ failure (1ms)
'errback' sync handler
✓ success (1ms)
✓ failure (1ms)
'errback' async handler
✓ success (6ms)
✓ failure (4ms)
gensync(function* () {})
✓ sync throw before body (1ms)
✓ sync throw inside body (1ms)
✓ async throw inside body (2ms)
✓ error inside body (2ms)
✓ successful return value (2ms)
✓ successful final value (1ms)
✓ yield unexpected object (1ms)
✓ yield suspend yield
✓ yield suspend return (1ms)
gensync.all()
✓ success (3ms)
✓ error first (1ms)
✓ error last (1ms)
gensync.race()
✓ success (2ms)
✓ error (1ms)
canary
✓ can run a test
markdownPreviewer
✕ should exist (3ms)
● markdownPreviewer › should exist
expect(received).toBeDefined()
Received: undefined
3601 | it("should exist", function() {
3602 | /* start new code */
> 3603 | expect(undefined).toBeDefined();
| ^
3604 | /* end new code */
3605 | });
3606 | });
at Object.<anonymous> (test/bundle.test.js:3603:23)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 30 passed, 31 total
Snapshots: 0 total
Time: 2.658s
Ran all test suites matching /test\/bundle.test.js/i.
error Command failed with exit code 1.
error Command failed with exit code 1.
As you can see, the stack trace Jest generates references a line of code in our bundle and not the test. Fortunately, we can get this by setting up devtool
in our test webpack config, which you'll recall is still totally separate. Let's keep it that way for now and use the inline-source-map
devtool we use in our dev configuration:
const path = require('path');
const glob = require('glob');
const testFiles = glob.sync("**/*.test.js").
filter(function(element) {
return element != "test/bundle.test.js";
}).map(function(element) {
return "./" + element;
});
module.exports = {
entry: testFiles,
/* start new code */
devtool: "inline-source-map",
/* end new code */
output: {
path: path.resolve(__dirname, "."),
filename: "bundle.test.js"
},
mode: "none"
};
Now, when we run our test again, we should see correct line numbers!
> rm test/bundle.test.js
> yarn test
yarn run v1.21.1
$ yarn webpack:test && yarn jest
$ webpack --config test/webpack.test.config.js --display-error-details
Hash: fa0080dd7d518bd83b2a
Version: webpack 4.41.5
Time: 479ms
Built at: 01/21/2020 5:47:39 PM
Asset Size Chunks Chunk Names
bundle.test.js 380 KiB 0 [emitted] main
Entrypoint main = bundle.test.js
[0] multi ./node_modules/gensync/test/index.test.js ./test/canary.test.js ./test/markdownPreviewer.test.js 52 bytes {0} [built]
[17] (webpack)/buildin/global.js 472 bytes {0} [built]
[52] ./test/canary.test.js 107 bytes {0} [built]
[53] ./test/markdownPreviewer.test.js 221 bytes {0} [built]
[54] ./js/markdownPreviewer.js 381 bytes {0} [built]
+ 52 hidden modules
$ jest test/bundle.test.js
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:201:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:221:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:241:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:261:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
FAIL test/bundle.test.js
gensync({})
option validation
✓ disallow async and errback handler together (3ms)
✓ disallow missing sync handler (1ms)
✓ errback callback required
generator function metadata
✓ automatic naming (1ms)
✓ explicit naming
✓ default arity
✓ explicit arity (1ms)
'sync' handler
✓ success (2ms)
✓ failure (3ms)
'async' handler
✓ success (3ms)
✓ failure (2ms)
'errback' sync handler
✓ success (1ms)
✓ failure (1ms)
'errback' async handler
✓ success (9ms)
✓ failure (2ms)
gensync(function* () {})
✓ sync throw before body (1ms)
✓ sync throw inside body (1ms)
✓ async throw inside body
✓ error inside body (1ms)
✓ successful return value (1ms)
✓ successful final value
✓ yield unexpected object (1ms)
✓ yield suspend yield
✓ yield suspend return
gensync.all()
✓ success (2ms)
✓ error first (1ms)
✓ error last (1ms)
gensync.race()
✓ success
✓ error (5ms)
canary
✓ can run a test
markdownPreviewer
✕ should exist (2ms)
● markdownPreviewer › should exist
expect(received).toBeDefined()
Received: undefined
at Object.<anonymous> (test/webpack:/test/markdownPreviewer.test.js:6:1)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 30 passed, 31 total
Snapshots: 0 total
Time: 2.67s, estimated 3s
Ran all test suites matching /test\/bundle.test.js/i.
error Command failed with exit code 1.
error Command failed with exit code 1.
Amazing, yeah? Let's now take some time to consolidate our Webpack configurations.
Consolidate Test Webpack Config
The more we start configuring Webpack, we run a risk of diverging critical things if our test configuration isn't kept up to date. Even though it's currently fairly different, let's consolidate it now so when we add more configuration we are forced to decide if that should apply to testing or not.
Create config/webpack.test.config.js
like so:
const path = require('path');
const glob = require('glob');
const Merge = require('webpack-merge');
const CommonConfig = require('./webpack.common.config.js');
const testFiles = glob.sync("**/*.test.js").
filter(function(element) {
return element != "test/bundle.test.js";
}).map(function(element) {
return "./" + element;
});
module.exports = Merge(CommonConfig, {
entry: testFiles,
output: {
path: path.join(__dirname, '../test'),
filename: 'bundle.test.js'
},
devtool: "inline-source-map",
mode: "none"
});
Let's delete the old file to avoid confusion:
> rm test/webpack.test.config.js
Now, we'll change our npm script in package.json
, so the scripts
section looks like so:
{
"scripts": {
"webpack": "webpack $npm_package_config_webpack_args",
"webpack:production": "webpack $npm_package_config_webpack_args --env=production",
"webpack:test": "webpack $npm_package_config_webpack_args --env=test",
"jest": "jest test/bundle.test.js",
"test": "yarn webpack:test && yarn jest"
}
}
And with that, yarn test
should still run (and fail showing us a good stack trace):
> yarn test
yarn run v1.21.1
$ yarn webpack:test && yarn jest
$ webpack $npm_package_config_webpack_args --env=test
Hash: b5747a2f282c34d827c0
Version: webpack 4.41.5
Time: 614ms
Built at: 01/21/2020 5:47:45 PM
Asset Size Chunks Chunk Names
bundle.test.js 380 KiB 0 [emitted] main
index.html 797 bytes [emitted]
Entrypoint main = bundle.test.js
[0] multi ./node_modules/gensync/test/index.test.js ./test/canary.test.js ./test/markdownPreviewer.test.js 52 bytes {0} [built]
[17] (webpack)/buildin/global.js 472 bytes {0} [built]
[52] ./test/canary.test.js 107 bytes {0} [built]
[53] ./test/markdownPreviewer.test.js 221 bytes {0} [built]
[54] ./js/markdownPreviewer.js 381 bytes {0} [built]
+ 52 hidden modules
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = index.html
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./html/index.html 961 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
+ 1 hidden module
$ jest test/bundle.test.js
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:201:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:221:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:241:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:261:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
FAIL test/bundle.test.js
gensync({})
option validation
✓ disallow async and errback handler together (2ms)
✓ disallow missing sync handler (1ms)
✓ errback callback required
generator function metadata
✓ automatic naming (2ms)
✓ explicit naming
✓ default arity
✓ explicit arity
'sync' handler
✓ success (2ms)
✓ failure (1ms)
'async' handler
✓ success (2ms)
✓ failure (1ms)
'errback' sync handler
✓ success
✓ failure (1ms)
'errback' async handler
✓ success (6ms)
✓ failure (3ms)
gensync(function* () {})
✓ sync throw before body
✓ sync throw inside body (1ms)
✓ async throw inside body
✓ error inside body (1ms)
✓ successful return value (1ms)
✓ successful final value
✓ yield unexpected object (1ms)
✓ yield suspend yield
✓ yield suspend return
gensync.all()
✓ success (2ms)
✓ error first (1ms)
✓ error last
gensync.race()
✓ success (1ms)
✓ error (3ms)
canary
✓ can run a test (1ms)
markdownPreviewer
✕ should exist (1ms)
● markdownPreviewer › should exist
expect(received).toBeDefined()
Received: undefined
at Object.<anonymous> (test/webpack:/test/markdownPreviewer.test.js:6:1)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 30 passed, 31 total
Snapshots: 0 total
Time: 1.26s, estimated 3s
Ran all test suites matching /test\/bundle.test.js/i.
error Command failed with exit code 1.
error Command failed with exit code 1.
Let's go ahead and undo our change to the test before we move on:
import markdownPreviewer from "../js/markdownPreviewer"
describe("markdownPreviewer", function() {
it("should exist", function() {
/* start new code */
expect(markdownPreviewer).toBeDefined();
/* end new code */
});
});
And now our tests are passing again:
> yarn test
yarn run v1.21.1
$ yarn webpack:test && yarn jest
$ webpack $npm_package_config_webpack_args --env=test
Hash: 1d379c020f905b5e56da
Version: webpack 4.41.5
Time: 647ms
Built at: 01/21/2020 5:47:50 PM
Asset Size Chunks Chunk Names
bundle.test.js 380 KiB 0 [emitted] main
index.html 797 bytes [emitted]
Entrypoint main = bundle.test.js
[0] multi ./node_modules/gensync/test/index.test.js ./test/canary.test.js ./test/markdownPreviewer.test.js 52 bytes {0} [built]
[17] (webpack)/buildin/global.js 472 bytes {0} [built]
[52] ./test/canary.test.js 107 bytes {0} [built]
[53] ./test/markdownPreviewer.test.js 229 bytes {0} [built]
[54] ./js/markdownPreviewer.js 381 bytes {0} [built]
+ 52 hidden modules
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = index.html
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./html/index.html 961 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
+ 1 hidden module
$ jest test/bundle.test.js
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:201:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:221:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:241:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
console.log node_modules/jest-jasmine2/build/jasmine/Env.js:502
● Test suite failed to run
Returning a Promise from "describe" is not supported. Tests must be defined synchronously.
Returning a value from "describe" will fail the test in a future version of Jest.
673 | var draining = false;
674 | var currentQueue;
> 675 | var queueIndex = -1;
| ^
676 |
677 | function cleanUpNextTick() {
678 | if (!draining || !currentQueue) {
at addSpecsToSuite (node_modules/jest-jasmine2/build/jasmine/Env.js:504:17)
at Suite.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:261:1)
at Object.<anonymous> (test/webpack:/node_modules/gensync/test/index.test.js:62:1)
at Object.<anonymous> (test/bundle.test.js:675:6)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at Object.<anonymous> (test/bundle.test.js:257:3)
at __webpack_require__ (test/webpack:/webpack/bootstrap:19:1)
at test/webpack:/webpack/bootstrap:83:1
at Object.<anonymous> (test/bundle.test.js:248:3)
Done in 5.74s.
PASS test/bundle.test.js
gensync({})
option validation
✓ disallow async and errback handler together (3ms)
✓ disallow missing sync handler
✓ errback callback required (1ms)
generator function metadata
✓ automatic naming (1ms)
✓ explicit naming
✓ default arity
✓ explicit arity (1ms)
'sync' handler
✓ success (1ms)
✓ failure (2ms)
'async' handler
✓ success (1ms)
✓ failure (1ms)
'errback' sync handler
✓ success (1ms)
✓ failure (1ms)
'errback' async handler
✓ success (10ms)
✓ failure (3ms)
gensync(function* () {})
✓ sync throw before body (1ms)
✓ sync throw inside body (1ms)
✓ async throw inside body (1ms)
✓ error inside body (2ms)
✓ successful return value (1ms)
✓ successful final value (1ms)
✓ yield unexpected object (1ms)
✓ yield suspend yield (1ms)
✓ yield suspend return
gensync.all()
✓ success (9ms)
✓ error first
✓ error last (1ms)
gensync.race()
✓ success (1ms)
✓ error
canary
✓ can run a test
markdownPreviewer
✓ should exist (1ms)
Test Suites: 1 passed, 1 total
Tests: 31 passed, 31 total
Snapshots: 0 total
Time: 2.873s
Ran all test suites matching /test\/bundle.test.js/i.
With what we have now, we can get really far, but let's add one more tweak to our dev environment, and configure auto-reloading of code as we make changes.