How to setup mocha to run tests in a Gatsby repo - javascript

I'm trying to get tests to run in a gatsby repo using Mocha, because we already have a lot of tests using mocha and chai, and we don't want to have 2 different assertion libraries, so we're not using Jest.
The first thing I did, is this:
npm i -D mocha chai #testing-library/react
Which installs mocha v8 and chai v4, Then I add a naive script in my package.json to see what happens:
"scripts": {
"test": "mocha --watch"
}
This gives me an error: unexpected token import for import { expect } from 'chai'; in my bare-bones test file. So next step, following Gatsby's conventions:
"scripts": {
"test": "npx --node-arg '-r esm' mocha --watch"
}
Okay, we're live, but no tests are running, next iteration:
"scripts": {
"test": "npx --node-arg '-r esm' mocha --watch 'src/**'"
}
Alright, now it crashes because of SyntaxError: Invalid or unexpected token for a <div> in a react component file.
At this point, I wonder if I really have to install babel and all of its machinery just to run a simple test, especially since gatsby does not use babel at all?
Does someone know of a really clean, modern setup that makes writing tests with mocha in Gatsby simple? Can esm be taught to read JSX without a pile of hacks?

At this point, I wonder if I really have to install babel and all of its machinery just to run a simple test
Unfortunately, yes.
, especially since gatsby does not use babel at all?
Actually that's not true, it's babel all the way down.
Thankfully, since Gatsby has already done all the tedious work of setting up babel, it's rather easy to set up your tests.
First, install the missing bits:
npm i -D #babel/register jsdom jsdom-global
Then add a fresh .babelrc at the root of the project with the following config:
{
"presets": ["babel-preset-gatsby"]
}
You don't need to install babel-preset-gatsby since it comes with all Gatsby setup.
That should be it. Now replace esm with #babel/register & register jsdom-global. Also make sure you set your NODE_ENV to test:
NODE_ENV=test npx mocha -r #babel/register -r jsdom-global/register ./src/**/*.test.js
And that should work.
When you add the test command to package.json, no needs to use npx:
"scripts": {
"test": "NODE_ENV=test mocha -r #babel/register -r jsdom-global/register ./src/**/*.test.js"
}
If you're testing Gatsby components, here's a few global variables you need to watch out for: __PATH_PREFIX__, __BASE_PATH__, and ___loader.
Mock them however you'd like, but for a quick and dirty test:
globalThis.__PATH_PREFIX__ = '/'
globalThis.__BASE_PATH__ = './'
globalThis.___loader = { enqueue: () => {} }

Related

How to setup lint-staged for Vue projects?

I created a new Vue3 app using the Vue CLI and selected Prettier for my linter config. I want to use commitlint, husky and lint-staged to validate commit messages and lint the code before pushing it.
What I did
Based on https://commitlint.js.org/#/guides-local-setup I setup commitlint with husky
npm install --save-dev #commitlint/{cli,config-conventional}
echo "module.exports = { extends: ['#commitlint/config-conventional'] };" > commitlint.config.js
npm install husky --save-dev
npx husky install
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
Based on https://github.com/okonet/lint-staged#installation-and-setup I setup lint-staged
npx mrm#2 lint-staged
and inside the package.json I replace
"lint-staged": {
"*.js": "eslint --cache --fix"
}
with
"lint-staged": {
"*": "npm run lint"
}
The problem
When modifying the README.md file in the project to
# my-repo
---
new commit
and try to commit that I get the following error message
> git -c user.useConfigOnly=true commit --quiet --allow-empty-message --file -
[STARTED] Preparing...
[SUCCESS] Preparing...
[STARTED] Running tasks...
[STARTED] Running tasks for *
[STARTED] npm run lint
[FAILED] npm run lint [FAILED]
[SUCCESS] Running tasks...
[STARTED] Applying modifications...
[SKIPPED] Skipped because of errors from tasks.
[STARTED] Reverting to original state because of errors...
[SUCCESS] Reverting to original state because of errors...
[STARTED] Cleaning up...
[SUCCESS] Cleaning up...
✖ npm run lint:
> my-repo#0.1.0 lint
> vue-cli-service lint "/home/.../my-repo/README.md"
error: Parsing error: Invalid character at README.md:1:1:
> 1 | # my-repo
| ^
2 |
3 | ---
4 |
1 error found.
npm ERR! code 1
npm ERR! path /home/my-repo
npm ERR! command failed
npm ERR! command sh -c lint-staged
npm ERR! A complete log of this run can be found in:
npm ERR! /home/.../.npm/_logs/2021-12-27T10_07_27_498Z-debug.log
husky - pre-commit hook exited with code 1 (error)
What it should do
Only fix the files that have been modified. The linter knows about files it is able to fix (js, ts, vue, html, ...).
When having a modified markdown file I get no errors when opening the terminal and run npm run lint. But I do get errors when using lint-staged with this setup "*": "npm run lint"
What is the correct setup for lint-staged to lint "lintable" files only?
Update regarding the comments
Other lint-staged syntaxes
I've suggested "**/*.{js,vue}": ["npm run lint:js:fix"], first of, lint:js:fix is subjective and up to you. This is what Kent C Dodds is using, so I'm just naming it in the same way.
But you could totally have lint:watermelon-potato-hehe instead, doesn't matter.
Now, about your propositions:
"**/*.{vue,js,jsx,ts,tsx}": "npm run lint", this one is targeting more extensions, which is totally fine. You may not really use .tsx/.jsx since it's not really popular among Vue devs.
About .ts itself, it may probably work good enough (maybe you'll need to add some plugins to your ESlint configuration). I'm not into TS so I can't really help on this one but it's out of the husky/lint-staged scope anyway.
Last time I started a Vue3 project, I've used Vitesse which has some nice defaults with TS, this may be a good start for you maybe.
As for the second part, since I like to setup my own ESlint config, with some simple and well documented API, we're using eslint --ext .js,.vue --fix. That way I'm sure of what is happening and how to troubleshoot it if needed.
vue-cli-service lint may be a good default package aimed towards Vue with some defaults, I'm not sure what's inside it and even if it's probably just an ESlint with some baked-in configuration, again we prefer to make our own Vue configuration with vanilla ESlint.
So yeah, if you need to go fast, use vue-cli-service lint for some quick linting, if you want to have a better flow in your project and want to fine grain your config, use vanilla ESlint, you'll get less trouble overall IMO.
"**/*.{vue,js,jsx,ts,tsx}": "eslint --ext .vue,.js,.jsx,.ts,.tsx --fix". On the right side, we globally have the same lint:js:fix scripts but with additional extensions.
So, you may ask why are we even writing the extensions on the left side for lint-staged and on the right side for lint:js:fix? I'd answer that those are not really needed on the right side (AFAIK), because lint-staged will only run the command to the left list of extensions.
Here, we wanted to be more explicit about the exact extensions we're targeting and also, it enables you to run npm run lint:js:fix in your CLI at any given point without getting errors on files ESlint is not handling (.txt, .json, .md, .jpg etc...).
So it could maybe be removed (not sure), fastest way to be sure is to try!
"**/*.{vue,js,jsx,ts,tsx}": "eslint --fix", this one may work fine as explained in the previous paragraph. Didn't tried it myself thought.
What about the other extensions?
Regarding .html, you should not have a lot of those in your Vue project. You could use the W3C validator to check for any errors if you really need it.
If you're speaking about your HTML in the template tags in your .vue files, those will be ESlint'ed properly. If you setup a Prettier on top of it, you will also get some nice auto-formatting which is really awesome to work with (once your team has agreed on a .prettierrc config).
Regarding .json files, those are not handled by ESlint. ESlint is only for JavaScript-ish files. If you want to lint/format your .json or even any other extensions at all, you can aim towards NPM, find a package that suits your team's needs and add it to your chain like "**/*.json": ["npm run lint-my-json-please"] and you should be good!
At the end, husky + lint-staged are not doing anything special really. They are tools to automate what you could write yourself in a CLI, so if it's working when done manually and you're happy with the result, you can put it in your config but you need to first found what the proper package and it's configuration.
In your package.json, you could have the following
"scripts": {
"lint:js": "eslint . --ext .js,.vue",
"lint:js:fix": "eslint --ext .js,.vue --fix",
},
In your .lintstagedrc
{
"**/*.{js,vue}": ["npm run lint:js:fix"]
}
In .husky/pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint-staged
In .husky/commit-msg
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install commitlint --edit ""
You can setup ESlint to watch any errors in your files in VScode (a lint + formatter when your files are saved is also doable pretty easily).
That way, you can run npm run lint:js to check the issues by yourself.
Otherwise, let husky run lint-staged and apply the eslint --fix to all of your .js and .vue files.
Your commitlint.config.js config should be okay!
As a reminder here, lint:js will scan all of your JS and Vue files.
While, when you commit and husky is executed (by running the lint:js:fix script), ONLY the files that you have touched will be linted (that's the whole point of lint-staged).

How to use jest.config.js with create-react-app

I would like to move my jest config out of my package.json, i am trying to use the --config as suggested here but get the error argv.config.match is not a function
package.json
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --config jest.config.js",
"eject": "react-scripts eject",
},
cli
hutber#hutber-mac:/var/www/management/node$ npm test -u
> management-fresh#0.1.0 test /var/www/management/node
> react-scripts test --config jest.config.js
Usage: test.js [--config=<pathToConfigFile>] [TestPathPattern]
argv.config.match is not a function
npm ERR! Test failed. See above for more details.
For me appending -- --config=jest.config.js worked.
So the whole string react-scripts test -- --config jest.config.js in your case.
TL;DR
Add -- before your options.
"test": "react-scripts test -- --config=jest.config.js",
The problem here is with react-scripts not seeing the options being passed to
it. We can demonstrate this by running it directly.
./node_modules/.bin/react-scripts test --config=jest.config.js
# argv.config.match is not a function
./node_modules/.bin/react-scripts test -- --config=jest.config.js
# This works.
Variations
How you pass options to scripts varies depending on which versions of npm or
Yarn you use. For completeness, here are the results for the variations:
# This runs, but completely ignores the option.
npm test --config=jest.config.js
# These result in "argv.config.match is not a function," indicating that the
# options were not understood.
npm test -- --config=jest.config.js
yarn test -- --config=jest.config.js
yarn test --config=jest.config.js
https://jestjs.io/docs/en/cli#using-with-yarn
https://jestjs.io/docs/en/cli#using-with-npm-scripts
create react app sets up the test script in package.json with
"test": "react-scripts test",
You can set additional options like so.
"test": "react-scripts test -- --config=jest.config.js",
Something like this might work if you want to send options through the CLI.
"test": "react-scripts test --",
yarn test --bail
# comes through as
react-scripts test -- --bail
Resources
Here are a few resources to explain the different usage.
https://medium.com/fhinkel/the-curious-case-of-double-dashes-b5e7711698f
For me adding jest as key in package.json file worked. Added all the required config as object in jest key rather than jest.config.js
"jest": {
"collectCoverageFrom": [
"src/**/*.js",
"!**/node_modules/**"
],
"coverageReporters": [
"text-summary",
"lcov",
"cobertura"
],
"testMatch": [
"**/*.test.js"
]
},
tldr
npm install jest --save-dev (not sure if this is required -- I just did it).
replace
"scripts": {
...
"test": "react-scripts test",
...
},
with
"scripts": {
...
"test": "jest --watch",
...
},
run tests as normal with npm test
Everything
Adding -- --config=jest.config.js sort of work for me: my tests passed, but then I was getting the following error (truncated):
Invalid testPattern --config=jest.config.js|--watch|--config|{"roots":["<rootDir>/src"]
...
Running all tests instead.
This problem is noted in the comment above.
Here's what's going on:
npm test looks in package.json for whatever is in scripts.test and runs that. For create-react-app, that's react-scripts test. This, in turn, runs
/node_modules/react-scripts/scripts/test.js (source) (you can easily print debug this to see what's going on). This script builds up a jest configuration based on your environment. When you add:
"test": "react-scripts test -- --config=jest.config.js",
to package.json, this replaces the jest config that react-scripts test is trying to create (yea!), but it also munges the arguments that "test": "react-scripts test" generates (boo!), so jest thinks you're trying to pass in a test pattern (which is obviously not a valid test pattern).
So, I decided to try running my tests using the jest CLI. At least for me, it worked fine and picked up all of my tests. It automatically looks for jest.config.js, so that works, and you can pass --watch in to get the same behavior as react-scripts test.
Keep in mind that react-scripts test seems to be going through a lot of trouble to build up a 'proper' config; I definitely haven't tried to figure all of that out: YMMV. Here's the full set of options it creates in my env. E.g., for --config the next element in the array is the config.
[
'--watch',
'--config',
'{"roots":["<rootDir>/src"],
"collectCoverageFrom":["src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts"],
"setupFiles":["<my_root_elided>/node_modules/react-app-polyfill/jsdom.js"],
"setupFilesAfterEnv":["<rootDir>/src/setupTests.js"],
"testMatch":["<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"],
"testEnvironment":"jsdom",
"testRunner":"<my_root_elided>/node_modules/jest-circus/runner.js",
"transform":{
"^.+\\\\.(js|jsx|mjs|cjs|ts|tsx)$":"<my_root_elided>/node_modules/react-scripts/config/jest/babelTransform.js",
"^.+\\\\.css$":"<my_root_elided>/node_modules/react-scripts/config/jest/cssTransform.js",
"^(?!.*\\\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)":"<my_root_elided>/node_modules/react-scripts/config/jest/fileTransform.js"},
"transformIgnorePatterns":["[/\\\\\\\\]node_modules[/\\\\\\\\].+\\\\.(js|jsx|mjs|cjs|ts|tsx)$",
"^.+\\\\.module\\\\.(css|sass|scss)$"],
"modulePaths":[],
"moduleNameMapper":{"^react-native$":"react-native-web",
"^.+\\\\.module\\\\.(css|sass|scss)$":"identity-obj-proxy"},
"moduleFileExtensions":["web.js", "js", "web.ts", "ts", "web.tsx", "tsx", "json", "web.jsx", "jsx", "node"],
"watchPlugins":["jest-watch-typeahead/filename", "jest-watch-typeahead/testname"],
"resetMocks":true,
"rootDir":"<my_root_elided>"}',
'--env',
'<my_root_elided>/node_modules/jest-environment-jsdom/build/index.js'
]
This one got me too! create react app is a bit tricky as it already contains jest. I removed the
--config jest.config.js line, and didn't need that extra test.config file.
I also made sure my enzyme file was named setupTests.js. The testing module will be specifically looking to run that file, so it must be named that. Also,I had to have it in my src/ folder, where before I had it in a src/test folder. If you are asking the above question you are probably past this point, but wanted to mention just in case. My setupTests.js looks like:
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({
adapter: new Adapter()
})
For me, none of the above answers worked. But with the help of documentation, I found out the way around.
For this purpose, place the code you want to configure jest, in your_project_root_folder/src/setupTests.js. My your_project_root_folder/src/setupTests.js looks like this
import Enzyme from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
Enzyme.configure({
adapter: new Adapter(),
})
And one more important point, you need to use enzyme-adapter-react-16 for react v16 and enzyme-adapter-react-15 for react v15
Moreover, if you want to use enzyme-to-json, you can place the following code in package.json file
"jest": {
"snapshotSerializers": ["enzyme-to-json/serializer"]
}
I would try adding "test": "jest --no-cache -w 2" to your package.json. Then run npm run test

adding unit testing to vue cli build

I've been adding some unit tests to my project recently and was wondering is it possible to add those unit tests to my yarn run build process. Or would it be better to create a bash script the runs both cmds like yarn run unit then yarn run build.
How would I go about this?
I'm using VueJs cli along with Jest for the unit testing
You can define additional script with hook 'pre' in package.json:
"scripts": {
...
"prebuild": "yarn run test"
...
}
So "prebuild" script will run before every build execution automatically.
Or you can just add another script and run it instead of build:
"scripts": {
...
"testbuild": "yarn run test && yarn run build"
...
}

Where to put webpack.config.js in React project?

I obtained already existing React project and I'm supposed to continue with it.I never used React before. I need to use web worker in the project - there's a project that suits my needs: Worker Loader
The suggest one adds this to their webpack.config.js:
module: {
rules: [
{
test: /\.worker\.js$/,
use: { loader: 'worker-loader' }
}
]
}
Then use worker like this:
import Worker from './file.worker.js';
const worker = new Worker();
But even though I can see that my React project uses webpack and babel o the background, there is no .babelrc or webpack.config.js in the project. This is how I run the project:
npm start
That actually runs react-scripts start based on what I see on package.json:
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
Neither webpack nor babel are in package.json dependencies, so I really have no idea how are they run by React. I would really appreciate if someone explained to me how does it work and how to configure webpack to use worker loader.
You need to run npm run eject first.
This command will copy all of your configuration files into your project(including webpack.config.js). However, run this command only if you MUST because although you will have full control over your configuration, you'll be on your own. React won't manage the configuration for you anymore.
Not sure 100%, but I think this app was created using react starter kit called Create React App and it comes with a package called react-scripts which is a wrapper around the build tools -- webpack being one of them. So lookup that stuff in npm. It might be all taken care of for you
Also, see what you have under node_modules directory --i am guessing it's all there

JS unit testing: run tests on file changes (like nodemon)

I have two question on JS unit testing:
1) Is there some tool that allows to automaticaly run javascript unit tests when certain files are changed (like for example nodemon restarts node.js on js changes).
2) Is this strategy appropriate (efficient) way to run unit tests?
Thanks,
Alex
For those who are committed to using nodemon, nodemon -x "npm test" has worked for me.
A little explanation
nodemon --help says:
-x, --exec app ........... execute script with "app", ie. -x "python -v".
In our case npm test is set to run tests by configuring our package.json
For example:
"scripts": {
"test": "mocha"
},
When using jest, nodemon is not necessary. Simply set the test script command to jest --watchAll in package.json as follows:
"scripts": {
"test": "jest --watchAll"
}
Check out grunt build system and the watch task. You can setup grunt to watch for file changes and then run any tasks you want (test, lint, compile, etc...).
https://github.com/cowboy/grunt
Some of the ideas are covered in this tutorial. http://javascriptplayground.com/blog/2012/04/grunt-js-command-line-tutorial
Here's a snippet of my package.json:
"scripts": {
"develop": "nodemon ./src/server.js --watch src --watch __tests__ -x \"yarn run test\"",
"test": "mocha ./__tests__/**/*.js --timeout 10000" }
The develop script is my command line to run my express server (entry: ./src/server.js), watch the /src directory which has all my server/API code, watch the /__tests__ directory which as all my specs and lastly tells nodemon to execute the enclosed statement before each restart/run with -x \"yarn run test\"
yarn run test is no different than npm run test. I prefer yarn over npm so that part is up to you. What is important is the \" tags inside the JSON value... without it, it will fail since the argument will be tokenized incorrectly.
This setup allows me to trigger changes from either server/API code or writing/fixing specs and trigger full test run BEFORE restarting the server via nodemon.
Cheers!

Categories