I want to use absolute paths for module imports. Thus I set cypress/tsconfig.json
like below
{
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"paths": {
"@api/*": [
"support/api/*"
]
},
"types": [
"cypress"
]
},
"include": [
"**/*.*"
]
}
And in my test code, the profile_api is in cypress/support/api/profile_api,
so the first import line:
import { getProfiles } from '@api/profile_api'
But always got the error:
Error: Cannot find module '@api/profile_api' from ...
Should be able to use absolute paths from tsconfig.json
I found cypress-webpack-preprocessor can fullfill it
In plugin file:
const webpack = require('@cypress/webpack-preprocessor')
module.exports = (on) => {
const options = {
// send in the options from your webpack.config.js, so it works the same
// as your app's code
webpackOptions: require('../../webpack.config'),
watchOptions: {},
}
on('file:preprocessor', webpack(options))
}
in webpack.config.js:
var path = require('path')
module.exports = {
resolve: {
alias: {
Api: path.resolve(__dirname, 'cypress/support/api'),
Helper: path.resolve(__dirname, 'cypress/support/helper'),
Obj: path.resolve(__dirname, 'cypress/support/obj'),
Fixtures: path.resolve(__dirname, 'cypress/fixtures'),
Plugins: path.resolve(__dirname, 'cypress/plugins')
}
}
}
Now, instead of using relative paths when importing like so:
import Utility from '../../api/utility';
I can use the alias:
import Utility from 'Api/utility';
Did you experience Cypress freezing up at all when running a spec file with an absolute import? If so, how did you get around this?
@ennisbenjamind No, everything looks good in this solution.
you have any other plugins set up by chance?
Alternatively, to references files relative to cypress
dir like so:
import * as h from '~/support/helpers.js'; // -> cypress/support/helpers.js
Do:
yarn add -D @cypress/webpack-preprocessor
cypress/plugins/index.js
:
const path = require('path');
const webpack = require('@cypress/webpack-preprocessor');
class CypressFileKindPlugin {
constructor(source, target) {
this.source = source;
this.target = target;
}
apply(resolver) {
resolver.plugin(this.source, (request, callback) => {
if (request.request.startsWith('~/')) {
const newPath = path.join(__dirname, '..', request.request.substr(2));
const newRelativePath = path.join(
request.relativePath,
path.relative(request.path, newPath));
const obj = Object.assign({}, request, {
path: newPath,
relativePath: newRelativePath,
request: undefined,
});
resolver.doResolve(this.target, obj, null, callback);
} else
return callback();
});
}
}
module.exports = (on, config) => {
on('file:preprocessor', webpack({
webpackOptions: {
resolve: {
plugins: [
new CypressFileKindPlugin('described-resolve', 'raw-file'),
],
}
},
}));
}
If you want to add babel
plugin, take a look at the default one, then:
module.exports = (on, config) => {
const webpackOptions = Object.assign({}, webpack.defaultOptions.webpackOptions, {
module: {
rules: [{
test: /\.js$/,
exclude: [/node_modules/],
use: [{
loader: 'babel-loader',
options: {
// babelrc: false, // in case you have .babelrc
// having .babelrc and passing options inline will result
// in a merge, potentially running plugins more than once
presets: ['env'],
plugins: ['transform-object-rest-spread'],
},
}],
}],
},
...
});
on('file:preprocessor', webpack({webpackOptions}));
}
For those of you using Create React App with react-scripts
webpack config, the following worked for me. I referenced the solution above and https://github.com/cypress-io/cypress-webpack-preprocessor/tree/master/examples/react-app.
module.exports = on => {
// find the Webpack config used by react-scripts
const webpackOptions = findWebpack.getWebpackOptions();
if (!webpackOptions) {
throw new Error('Could not find Webpack in this project 馃槩');
}
const cleanOptions = {
reactScripts: true
};
findWebpack.cleanForCypress(cleanOptions, webpackOptions);
const options = {
webpackOptions,
watchOptions: {}
};
// Define your alias(es) here:
options.webpackOptions.resolve.alias.cypress = path.resolve(process.cwd(), 'cypress');
on('file:preprocessor', webpackPreprocessor(options));
}
Cypress now uses ts-node instead of webpack, so I tried another method at solving this.
ts-node
does not support the paths option https://github.com/TypeStrong/ts-node/issues/138, so my workaround is to use the tsconfig-paths
package. It has the advantage of reusing the paths
from your top-level tsconfig, so you don't have to duplicate them in the cypress tsconfig.json
Cypress 5.0
tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
// Add your paths here
}
}
}
cypress/tsconfig.json
{
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": "..",
}
}
cypress/plugins/index.ts
// Make sure this is first
import "./register-tsconfig-paths"
export default function (on, config) {
return config
}
cypress/plugins/register-tsconfig-paths
import tsConfig from "../../tsconfig.json"; // Your top-level config!
import * as tsConfigPaths from "tsconfig-paths";
const baseUrl = "./";
tsConfigPaths.register({
baseUrl,
paths: tsConfig.compilerOptions.paths
});
I just started using this workaround so I'm not sure if its bullet-proof, hopefully this saves someone an afternoon of head scratching.
EDIT: Two months later, we haven't had any issues. For our solution we need to pass custom options to tsconfig-paths
. If you don't need to, use the more simpler solution @flybayer posted below
@marklawlor oh hallelujah!! Finally found your comment after trying several outdated blog post.
This was all I needed to make it work:
require("tsconfig-paths").register()
"e2e": "NODE_ENV=src cypress open"
worked for me
"e2e": "NODE_ENV=src cypress open"
worked for me
I would recommend against using this solution. Using a nonstandard NODE_ENV
could have unintended consequences (eg NextJS will error with a NONSTANDARD_NODE_ENV_ERROR
, webpack will bundle differently, etc). This also prevents you from easily using NODE_ENV
flags in your own codebase.
Most helpful comment
I found cypress-webpack-preprocessor can fullfill it
In plugin file:
in webpack.config.js:
Now, instead of using relative paths when importing like so:
I can use the alias: