resolve modules in testcafe typescript tests - reactjs

I encountered the module resolving issue in my testcafe(1.17.1) test.
I have a test
//test.e2e.ts in <root>/tests folder
import {MyComponent} from '../src/MyComponent';
...
...
fixture('Custom domain tests');
test('test 1', async () => {
...
...
});
the <root>/src/MyComponent.tsx looks like this
//MyComponent.tsx in <root>/src folder
import React, { FunctionComponent } from 'react';
import styles from '../scss/MyComponent.module.scss';
export const MyComponent: FunctionComponent = () => {
...
...
}
When I run testcafe ./tests/test.e2e.ts, I always got the error
Error: TypeScript compilation failed. Cannot find module '../scss/MyComponent.module.scss' or its corresponding type declarations.
My testcafe tsconfig specified the path config, the <root>/scss-module-for-tests.ts just exports a empty string
// .testcaferc.json in <root>/
{
"compilerOptions": {
"typescript": {
"options": {
"esModuleInterop": true,
"baseUrl": ".",
"paths": {
"../scss/MyComponent.module.scss": ["scss-module-for-tests.ts"],
"*.scss": ["scss-module-for-tests.ts"]
}
}
}
}
}
However, seems typescript path config doesn't resolve relative path nor accept a regex, but My project has a lot of those ../scss/*module.scss imports. Is there anyway to resolve the scss file directly for testcafe?
Thanks in advance!
Update on 12/02/2021
I tried add compiler for testcafe according to this doc , I put a .testcaferc.json at the root folder
//.testcaferc.json at <root>
{
"compilerOptions": {
"typescript": {
"options": {
"esModuleInterop": "true"
}
}
}
}
But seems the esModuleInterop=true config didn't reach to testcafe.
Update 12/13/2021:
Not easy to configure it correctly, I removed the *.scss file reference in the test code. Problem solved.

It looks like your TestCafe tests' code references your testing application code. However, TestCafe tests do not require testing application code. In fact, the code of the TestCafe tests and the application can be divided into two separate projects. Please try to separate your web application code and your TestCafe tests' code.

Related

Can't resolve 'url' in node_modules error when trying to access Azure Key Vault with React

I'm new to React and Azure, I'm trying to get a secret value from Azure [Key Vault]. I'm getting this compile error.
I'm using: "#azure/identity": "^2.0.4", "#azure/keyvault-secrets": "^4.4.0"
import React, { useState } from 'react';
import { SecretClient } from "#azure/keyvault-secrets";
import { DefaultAzureCredential } from "#azure/identity";
export const KV = () => {
const [secret, setSecret] = useState("");
const credential = new DefaultAzureCredential();
const client = new SecretClient("https://xxx.vault.azure.net/", credential);
client.getSecret("secretKey").then(res => { setSecret(res); });
return <>{secret}</>
}
I'm getting this error message:
ERROR in
./node_modules/#azure/keyvault-secrets/dist-esm/keyvault-common/src/parseKeyvaultIdentifier.js
3:0-27 Module not found: Error: Can't resolve 'url' in
'xxx\node_modules#azure\keyvault-secrets\dist-esm\keyvault-common\src'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js
core modules by default. This is no longer the case. Verify if you
need this module and configure a polyfill for it. If you want to
include a polyfill, you need to:
add a fallback 'resolve.fallback: { "url": require.resolve("url/") }'
install 'url' If you don't want to include a polyfill, you can use an empty module like this: resolve.fallback: { "url": false }
<Add "lib": ["dom"] to your tsconfig.json, or simply add dom to an existing array in the lib property>, but I'm not using typescript. How do I do this in react?
You can find tsconfig.json inside the root folder of your react project.
If you don't have tsconfig.json, you can create the same and add "lib":["dom"] to it.
For example:
{
"extends": "../../../tsconfig.package",
"compilerOptions": {
"declarationDir": "./types",
"outDir": "./dist-esm",
"lib": ["dom"],
"resolveJsonModule": true,
"paths": {
"#azure/keyvault-secrets": ["./src/index"]
}
},
"include": [
"./src/**/*.ts",
"./test/**/*.ts",
"../keyvault-common/**/*.ts",
"samples-dev/**/*.ts"
]
}
You can refer to Where can I find tsconfig.json file in my react native project, tsconfig.json and Cannot find name 'URL'

How to import a json file in typescript? [duplicate]

I have a JSON file that looks like following:
{
"primaryBright": "#2DC6FB",
"primaryMain": "#05B4F0",
"primaryDarker": "#04A1D7",
"primaryDarkest": "#048FBE",
"secondaryBright": "#4CD2C0",
"secondaryMain": "#00BFA5",
"secondaryDarker": "#009884",
"secondaryDarkest": "#007F6E",
"tertiaryMain": "#FA555A",
"tertiaryDarker": "#F93C42",
"tertiaryDarkest": "#F9232A",
"darkGrey": "#333333",
"lightGrey": "#777777"
}
I'm trying to import it into a .tsx file. For this I added this to the type definition:
declare module "*.json" {
const value: any;
export default value;
}
And I'm importing it like this.
import colors = require('../colors.json')
And in the file, I use the color primaryMain as colors.primaryMain. However I get an error:
Property 'primaryMain' does not exist on type 'typeof "*.json"
With TypeScript 2.9.+ you can simply import JSON files with benefits like typesafety and intellisense by doing this:
import colorsJson from '../colors.json'; // This import style requires "esModuleInterop", see "side notes"
console.log(colorsJson.primaryBright);
Make sure to add these settings in the compilerOptions section of your tsconfig.json (documentation):
"resolveJsonModule": true,
"esModuleInterop": true,
Side notes:
Typescript 2.9.0 has a bug with this JSON feature, it was fixed with 2.9.2
The esModuleInterop is only necessary for the default import of the colorsJson. If you leave it set to false then you have to import it with import * as colorsJson from '../colors.json'
The import form and the module declaration need to agree about the shape of the module, about what it exports.
When you write (a suboptimal practice for importing JSON since TypeScript 2.9 when targeting compatible module formatssee note)
declare module "*.json" {
const value: any;
export default value;
}
You are stating that all modules that have a specifier ending in .json have a single export named default.
There are several ways you can correctly consume such a module including
import a from "a.json";
a.primaryMain
and
import * as a from "a.json";
a.default.primaryMain
and
import {default as a} from "a.json";
a.primaryMain
and
import a = require("a.json");
a.default.primaryMain
The first form is the best and the syntactic sugar it leverages is the very reason JavaScript has default exports.
However I mentioned the other forms to give you a hint about what's going wrong. Pay special attention to the last one. require gives you an object representing the module itself and not its exported bindings.
So why the error? Because you wrote
import a = require("a.json");
a.primaryMain
And yet there is no export named primaryMain declared by your "*.json".
All of this assumes that your module loader is providing the JSON as the default export as suggested by your original declaration.
Note: Since TypeScript 2.9, you can use the --resolveJsonModule compiler flag to have TypeScript analyze imported .json files and provide correct information regarding their shape obviating the need for a wildcard module declaration and validating the presence of the file. This is not supported for certain target module formats.
Here's how to import a json file at runtime
import fs from 'fs'
var dataArray = JSON.parse(fs.readFileSync('data.json', 'utf-8'))
This way you avoid issues with tsc slowing down or running out of memory when importing large files, which can happen when using resolveJsonModule.
It's easy to use typescript version 2.9+. So you can easily import JSON files as #kentor decribed.
But if you need to use older versions:
You can access JSON files in more TypeScript way. First, make sure your new typings.d.ts location is the same as with the include property in your tsconfig.json file.
If you don't have an include property in your tsconfig.json file. Then your folder structure should be like that:
- app.ts
+ node_modules/
- package.json
- tsconfig.json
- typings.d.ts
But if you have an include property in your tsconfig.json:
{
"compilerOptions": {
},
"exclude" : [
"node_modules",
"**/*spec.ts"
], "include" : [
"src/**/*"
]
}
Then your typings.d.ts should be in the src directory as described in include property
+ node_modules/
- package.json
- tsconfig.json
- src/
- app.ts
- typings.d.ts
As In many of the response, You can define a global declaration for all your JSON files.
declare module '*.json' {
const value: any;
export default value;
}
but I prefer a more typed version of this. For instance, let's say you have configuration file config.json like that:
{
"address": "127.0.0.1",
"port" : 8080
}
Then we can declare a specific type for it:
declare module 'config.json' {
export const address: string;
export const port: number;
}
It's easy to import in your typescript files:
import * as Config from 'config.json';
export class SomeClass {
public someMethod: void {
console.log(Config.address);
console.log(Config.port);
}
}
But in compilation phase, you should copy JSON files to your dist folder manually. I just add a script property to my package.json configuration:
{
"name" : "some project",
"scripts": {
"build": "rm -rf dist && tsc && cp src/config.json dist/"
}
}
In my case I needed to change tsconfig.node.json:
{
"compilerOptions": {
...
"resolveJsonModule": true
},
"include": [..., "colors.json"]
}
And to import like that:
import * as colors from './colors.json'
Or like that:
import colors from './colors.json'
with "esModuleInterop": true
You should add
"resolveJsonModule": true
as part of compilerOptions to tsconfig.json.
Often in Node.js applications a .json is needed. With TypeScript 2.9, --resolveJsonModule allows for importing, extracting types from and generating .json files.
Example #
// tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"resolveJsonModule": true,
"esModuleInterop": true
}
}
// .ts
import settings from "./settings.json";
settings.debug === true; // OK
settings.dry === 2; // Error: Operator '===' cannot be applied boolean and number
// settings.json
{
"repo": "TypeScript",
"dry": false,
"debug": false
}
by: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html
Another way to go
const data: {[key: string]: any} = require('./data.json');
This was you still can define json type is you want and don't have to use wildcard.
For example, custom type json.
interface User {
firstName: string;
lastName: string;
birthday: Date;
}
const user: User = require('./user.json');
In an Angular (typescript) app, I needed to include a .json file in my environment.ts. To do so, I had to set two options in tsconfig:
{
"compilerOptions": {
"moduleResolution": "node",
"resolveJsonModule": true
}
}
Then, I could import my json file into the environment.ts:
import { default as someObjectName } from "../some-json-file.json";
You can import a JSON file without modifying tsconfig you tell explicitly that you are importing JSON
import mydata from './mydataonfile.json' assert { type: "json" };
I know this does not fully answer the question but many people come here to know how to load JSON directly from a file.
Enable "resolveJsonModule": true in tsconfig.json file and implement as below code, it's work for me:
const config = require('./config.json');
Note that if you using #kentor ways
Make sure to add these settings in the compilerOptions section of your tsconfig.json (documentation):
You need to add --resolveJsonModule and--esModuleInterop behind tsc command to compile your TypeScript file.
Example:
tsc --resolveJsonModule --esModuleInterop main.ts
require is a common way to load a JSON file in Node.js
in my case I had to change: "include": ["src"] to "include": ["."] in addition to "resolveJsonModule":true because I tried to import manifest.json from the root of the project and not from ./src

Cypress test fails to run when importing from source code of CRA+TS app

One of my Cypress tests fails to run when it tries to import from a file in the source code of a create-react-app src directory.
The test looks like:
// cypress/integration/this-fails.js
import { MY_CONSTANT } from '../../src/constants';
describe('Cypress', () => {
...
})
The imported source file looks like:
// src/constants.ts
export const MY_CONSTANT = 'foo';
The Cypress test failure is caused by a Jest test file in the source directory:
ERROR in /my-app/src/App.test.tsx(5,1)
Cannot find name 'test'. Do you need to install type definitions for a test runner? Try`npm i #types/jest`or`npm i #types/mocha`.
The Jest type definitions are installed. Additionally, to no avail, I have tried to exclude the problematic Jest test in the Cypress tsconfig.
// cypress/tsconfig.json
{
...
"exclude": [
"../src/App.test.tsx"
],
...
}
Here is a minimal repo that reproduces my problem.
Lastly, to clarify why I am importing things into Cypress tests from the source directory — the imported variable is intended to be a DOM selector or a function that returns a DOM selector so that selectors are not hardcoded in the tests.
I'm not sure why the message is TypeScript emitted no output for /my-app/src/constants.ts, this seems to indicate that the file is readable and typescript attempts to parse it, and does not recognize the syntax.
However my guess is that the code of the test is running in a browser process and can't access files outside of it's folder.
If constant.ts is in cypress/fixtures it works, so one easy way is to add a script to copy the file. A script called "precypress" will be automatically run when the "cypress" script is invoked.
This is kind of 90% there - you don't get hot-module reload when constants.ts changes.
package.json
"scripts": {
...
"precypress": "copyfiles ./src/constants.ts ./cypress/fixtures",
"cypress": "cypress open"
},
It also works with functions and handles typing,
test
import { MY_CONSTANT, getMyConstant } from '../fixtures/src/constants';
describe('Cypress', () => {
it('is working', () => {
cy.visit('/')
alert(MY_CONSTANT);
alert(getMyConstant());
expect(true).to.equal(true)
})
})
constant.ts
export const MY_CONSTANT: Number = 10;
export const getMyConstant: Function = () => 20;

react jsconfig.json ignores paths

I have the following jsconfig.json in the root of my react app:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"rmv": ["components/rmv/*"]
}
}
}
and there is a helper.jsx file located in ./src/components/rmv folder.
But my attempts to export it directly like that:
import Helper from 'rmv/helper'
fail with an error:
Failed to compile
Module not found: Can't resolve 'rmv/helper'
Only import Helper from 'components/rmv/helper' works.
Why?
PS: I also tried:
"paths": {
"rmv/*": ["components/rmv/*"]
}
Does not work either.
Here is the minimum reproducible example: github.com/chapkovski/trouble_with_jsconfig
Specifically these lines:
https://github.com/chapkovski/trouble_with_jsconfig/blob/e37c8c216eac0da7c70023f7fba47eea54973176/src/App.js#L4-L5
Paths are currently unavailable in apps made with create-react-app:
https://github.com/facebook/create-react-app/issues/5645
You may want to consider using rescripts to let you modify your configuration in CRA 2+ apps.
The paths need to be relative to the baseUrl. Your baseUrl has a value of ./src which is good. However, your paths listed in the array for rmv/* are NOT relative paths, as they don't start with a relative location (./ or ../).
I would encourage you to try prefixing ./ onto your paths.
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"rmv/*": ["./components/rmv/*"]
}
}
}
I found this documentation on the subject: Using webpack aliases
[Eject CRA] You could use webpack alias as alternative solution for the use case.
In webpack.config.js
module.exports = {
//...
resolve: {
alias: {
rmv: path.resolve(__dirname, 'src/components/rmv/')
}
}
};
Now, you could import Helper component as bellow:
import Helper from 'rmv/helper';

How to use jest with webpack?

I use webpack to develop a React component. Here is a simple version of it:
'use strict';
require('./MyComponent.less');
var React = require('react');
var MyComponent = React.createClass({
render() {
return (
<div className="my-component">
Hello World
</div>
);
}
});
module.exports = MyComponent;
Now, I would like to test this component using jest. Here is the relevant bit from my package.json:
"scripts": {
"test": "jest"
},
"jest": {
"rootDir": ".",
"testDirectoryName": "tests",
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"unmockedModulePathPatterns": [
"react"
]
}
When running npm test, I get the following error:
SyntaxError: /Users/mishamoroshko/react-component/src/tests/MyComponent.js: /Users/mishamoroshko/react-component/src/MyComponent.js: /Users/mishamoroshko/react-component/src/MyComponent.less: Unexpected token ILLEGAL
Looks like webpack needs to process require('./MyComponent.less') before jest can run the test.
I wonder if I need to use something like jest-webpack. If yes, is there a way to specify multiple scriptPreprocessors? (note that I already use babel-jest)
The cleanest solution I found for ignoring a required module is to use the moduleNameMapper config (works on the latest version 0.9.2)
The documentation is hard to follow. I hope the following will help.
Add moduleNameMapper key to your packages.json config. The key for an item should be a regex of the required string. Example with '.less' files:
"moduleNameMapper": { "^.*[.](less|LESS)$": "EmptyModule" },
Add a EmptyModule.js to your root folder:
/**
* #providesModule EmptyModule
*/
module.exports = '';
The comment is important since the moduleNameMapper use EmptyModule as alias to this module (read more about providesModule).
Now each require reference that matches the regex will be replaced with an empty string.
If you use the moduleFileExtensions configuration with a 'js' file, then make sure you also add the EmptyModule to your 'unmockedModulePathPatterns'.
Here is the jest configuration I ended up with:
"jest": {
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"moduleFileExtensions": ["js", "json","jsx" ],
"moduleNameMapper": {
"^.*[.](jpg|JPG|gif|GIF|png|PNG|less|LESS|css|CSS)$": "EmptyModule"
},
"preprocessorIgnorePatterns": [ "/node_modules/" ],
"unmockedModulePathPatterns": [
"<rootDir>/node_modules/react",
"<rootDir>/node_modules/react-dom",
"<rootDir>/node_modules/react-addons-test-utils",
"<rootDir>/EmptyModule.js"
]
}
I ended up with the following hack:
// package.json
"jest": {
"scriptPreprocessor": "<rootDir>/jest-script-preprocessor",
...
}
// jest-script-preprocessor.js
var babelJest = require("babel-jest");
module.exports = {
process: function(src, filename) {
return babelJest.process(src, filename)
.replace(/^require.*\.less.*;$/gm, '');
}
};
But, I'm still wondering what is the right solution to this problem.
I just found that it's even simpler with Jest's moduleNameMapper configuration.
// package.json
"jest": {
"moduleNameMapper": {
"^.+\\.scss$": "<rootDir>/scripts/mocks/style-mock.js"
}
}
// style-mock.js
module.exports = {};
More detail at Jest's tutorial page.
I recently released Jestpack which might help. It first builds your test files with Webpack so any custom module resolution/loaders/plugins etc. just work and you end up with JavaScript. It then provides a custom module loader for Jest which understands the Webpack module runtime.
From Jest docs:
// in terminal, add new dependency: identity-obj-proxy
npm install --save-dev identity-obj-proxy
// package.json (for CSS Modules)
{
"jest": {
"moduleNameMapper": {
"\\.(css|less)$": "identity-obj-proxy"
}
}
}
The snippet above will route all .less files to the new dependency identity-obj-proxy, which will return a string with the classname when invoked, e.g. 'styleName' for styles.styleName.
I think a less hacky solution would be to wrap your preprocessor in a conditional on the filename matching a javascript file:
if (filename.match(/\.jsx?$/)) {
return babelJest.process(src, filename);
} else {
return '';
}
This works even if you don't explicitly set the extension in the require line and doesn't require a regex substitution on the source.
I have experienced similar issue with such pattern
import React, { PropTypes, Component } from 'react';
import styles from './ContactPage.css';
import withStyles from '../../decorators/withStyles';
#withStyles(styles)
class ContactPage extends Component {
see example at https://github.com/kriasoft/react-starter-kit/blob/9204f2661ebee15dcb0b2feed4ae1d2137a8d213/src/components/ContactPage/ContactPage.js#L4-L7
For running Jest I has 2 problems:
import of .css
applying decorator #withStyles (TypeError: <...> (0 , _appDecoratorsWithStyles2.default)(...) is not a function)
First one was solved by mocking .css itself in script preprocessor.
Second one was solved by excluding decorators from automocking using unmockedModulePathPatterns
module.exports = {
process: function (src, filename) {
...
if (filename.match(/\.css$/)) src = '';
...
babel.transform(src, ...
}
}
example based on https://github.com/babel/babel-jest/blob/77a24a71ae2291af64f51a237b2a9146fa38b136/index.js
Note also: when you working with jest preprocessor you should clean cache:
$ rm node_modules/jest-cli/.haste_cache -r
Taking inspiration from Misha's response, I created an NPM package that solves this problem while also handling a few more scenarios I came across:
webpack-babel-jest
Hopefully this can save the next person a few hours.
If you're using babel, you can strip unwanted imports during the babel transform using something like https://github.com/Shyp/babel-plugin-import-noop and configuring your .babelrc test env to use the plugin, like so:
{
"env": {
"development": {
...
},
"test": {
"presets": [ ... ],
"plugins": [
["import-noop", {
"extensions": ["scss", "css"]
}]
]
}
}
}
We had a similar problem with CSS files. As you mentioned before jest-webpack solves this problem fine. You won't have to mock or use any module mappers either. For us we replaced our npm test command from jest to jest-webpack and it just worked.
Webpack is a great tool, but I don't need to test it's behavior with my Jest unit tests, and adding a webpack build prior to running unit tests is only going to slow down the process. The text-book answer is to mock non-code dependencies using the "moduleNameMapper" option
https://facebook.github.io/jest/docs/webpack.html#handling-static-assets

Resources