debug jest with source-map support - unit-testing

I'm using jest with babel and i want to debug my test.
I'm able to debug jest with node --debug-brk ./node_modules/.bin/jest --runInBand [any other arguments here] but lose all es6 source code info.
I know source-map-support is a babel feature not jest's, but jest should somehow tell babel to include source-map-support.
so far I've tried
config transform for jest
"transform": {
".*": "/scripts/jest/preprocessor.js"
}
in my preprocess.js file I have
const register=require("babel-register")
register();
....
module.exports = {
process: function(src, filename) {
const options = assign({}, babelOptions, {
filename: filename,
retainLines: true,
sourceMap:true, // I added this line
});
return babel.transform(src, options).code;
}
but this doesn't work. any ideas?

Related

Jest mock #electron/remote module

I have some problems with my tests since I upgraded from electron v11 to v20.
With this update the remote was moved from electron to a new module #electron/remote.
In jest.config.js I have the electron module mock mapping: electron: '<rootDir>/tests/mock/electron.mock.ts'
...
const mockIpcRenderer = {
on: jest.fn(),
send: jest.fn()
};
const mockRemote = {
app: {
getPath: mockGetPath,
getAppPath() {
return '/app/working/path';
}
},
process: {
env: jest.fn()
}
};
...
export const ipcRenderer = mockIpcRenderer;
export const remote = mockRemote;
So, after the update I extracted the remote part from the mock to a new file: remote.mock.ts
const mockApp = {
getPath: mockGetPath,
getAppPath() {
return '/app/working/path';
}
};
const mockProcess = {
env: jest.fn()
};
function mockGetPath(path: string) {
return 'somtething';
}
export const app = mockApp;
export const process = mockProcess;
and I added this line to my test file:
jest.mock('#electron/remote', () => require('../mock/remote.mock'));
The problem is when I run the test I get TypeError: Cannot read property 'on' of undefined.
The ipcRenderer is undefined and I don't know why ?
When I added that jest.mock('#electron/remote', () => require('../mock/remote.mock')); to the test file, the mocks from electron.mock.ts are not defined anymore, for some reason.
In the jest.config.js I added start and end symbols and added a new line for #electron/remote and it works.
moduleNameMapper: {
'^#electron/remote$': '<rootDir>/tests/mock/electron.mock.ts',
'^electron$': '<rootDir>/tests/mock/electron.mock.ts',
},

How to use CodeceptJS to unit-test a JS function

I've set up CodeceptJS for a project and use it to test various end-to-end scenarios.
Now I want to extend the tests-suite to also run unit-tests to verify functionality of custom JS functions.
For example: I have a global object App that has a version attribute. As a first test, I want to confirm that App.version is present and has a value.
My first attempt is a test.js file with the following code:
Feature('Unit Tests');
Scenario('Test App presence', ({ I }) => {
I.amOnPage('/');
I.executeScript(function() {return App.version})
.then(function(value) { I.say(value) } );
});
Problems with this code
The major issue: How can I assert that the App.version is present?
My script can display the value but does not fail if it's missing
My code is very complex for such a simple test.
I'm sure there's a cleaner/faster way to perform that test, right?
Here is a solution that works for me:
Read data from the browser:
I created a custom helper via npx codecept gh and named it BrowserAccess.
The helper function getBrowserData uses this.helpers['Puppeteer'].page.evaluate() to run and return custom code from the browser scope. Documentation for .evaluate()
Custom assertions:
Install the codeceptjs-assert package, e.g. npm i codeceptjs-assert
Add the AssertWrapper-helper to the codecept-config file. This enables checks like I.assert(a, b)
Full Code
codecept.conf.js
exports.config = {
helpers: {
AssertWrapper: {
require: "codeceptjs-assert"
},
BrowserAccess: {
require: './browseraccess_helper.js'
},
...
},
...
}
browseraccess_helper.js
const Helper = require('#codeceptjs/helper');
class BrowserAccess extends Helper {
async getBrowserData(symbolName) {
const currentPage = this.helpers['Puppeteer'].page;
let res;
try {
res = await currentPage.evaluate((evalVar) => {
let res;
try {
res = eval(evalVar);
} catch (e) {
}
return Promise.resolve(res);
}, symbolName);
} catch (err) {
res = null;
}
return res;
}
}
jsapp_test.js (the test is now async)
Feature('Unit Tests');
Scenario('Test App presence', async ({ I }) => {
I.amOnPage('/');
const version = await I.getBrowserData('App.version');
I.assertOk(version);
});

Jest unit test issue: "module not found"

Currently Jest is failing the tests because it cannot find the module called inside a component:
FAIL tests/Unit/VHeaderBar.spec.ts
● Test suite failed to run
Cannot find module '##/public/assets/images/placeholder.png' from 'VHeaderBar.vue'
at Resolver.resolveModule (node_modules/jest-runtime/node_modules/jest-resolve/build/index.js:221:17)
at src/components/VHeaderBar.vue:687:18
at Object.<anonymous> (src/components/VHeaderBar.vue:749:3)
Case
In NuxtJs the ## signs refer to the root directory, because in the end solution we want to store images in the public folder or storage folder, which is located in the root of the project.
When running tests jest checks the src folder, then tries to mount the images stored from the root and can't find them.
I have tried many different ways to fix this issue, but can't seem to find the solution.
Here's a shortlist of what I already tried:
Changing regex to check for image files and lead it to the correct folder using the moduleNameMapper option in the Jest config file.
I read something on Stack about using a "mock" folder that exports the images files through javascript, but that didn't work.
Using the modulePaths option in the Jest config file.
Creating an alias in the tsconfig.js for the assets folder and using that in the moduleNameMapper
Tried a different approach in the VueJS component and test file to load assets, which broke the compiling process (so I reverted that).
Current Jest Config file
module.exports = {
moduleFileExtensions: [
"ts",
"tsx",
"vue",
"js",
"json"
],
watchman: false,
moduleNameMapper: {
"/\.(gif|jpg|jpeg|tiff|png)$/i": "<rootDir>/public/assets/images/$1",
"^#/(.*)$": "<rootDir>/src/$1",
"^~/(.*)$": "<rootDir>/src/$1",
"^~~/(.*)$": "<rootDir>/src/$1"
},
transform: {
// process js with `babel-jest`
"^.+\\.js$": "<rootDir>/node_modules/babel-jest",
// process `*.vue` files with `vue-jest`
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
// process `*.ts` files with `ts-jest`
"^.+\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest",
},
snapshotSerializers: [
"<rootDir>/node_modules/jest-serializer-vue"
],
collectCoverage: true,
collectCoverageFrom: [
"<rootDir>/src/components/**/*.vue",
"<rootDir>/src/pages/**/*.vue",
"<rootDir>/src/layouts/**/*.vue"
],
testMatch: [
'**/tests/Unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
}
Current folder structure (only folders we use for the test)
project folder
- public
-- assets
--- **images**
- src
-- components
--- **mounted component** (works)
- tests
-- Unit
--- mountedComponent.spec.ts
Any suggestions?
Do I fix the jest.config?
Is there something wrong with the syntax?
Do I have to fix the tsconfig?
I've had a similar issue and it goes down to typescript not being able to import that file.
I've solved it by adding file type definition to files.d.ts:
declare module "*.pdf" {
const file: Buffer;
export default file;
}
declare module "*.jpeg" {
const src: string;
export default src;
}
declare module "*.png" {
const src: string;
export default src;
}
Referring to this file in tsconfig.json:
{
"compilerOptions": {
/* ... */
},
"files": ["./src/#types/files.d.ts"],
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
And adding file transforms to jest.config.js:
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
roots: ["<rootDir>/src/"],
moduleNameMapper: {
"#app/(.*)": "<rootDir>/src/$1",
"#lib/(.*)": "<rootDir>/src/lib/$1",
},
transform: { // Transforms here
"\\.(gql|graphql)$": "#jagi/jest-transform-graphql",
"\\.(html|html|txt|pem|key)$": "./jest-transform-text.js",
"\\.(p12|pdf|otf|ttf)$": "./jest-transform-buffer.js",
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
},
coverageReporters: ["text", "lcov"],
};
Examples of transform files:
// jest-transform-buffer.js
"use strict";
const fs = require("fs");
module.exports = {
process(src, filename) {
const data = fs.readFileSync(filename, "hex");
return (
'module.exports=Buffer.from("' +
data +
'","hex");module.exports.default=module.exports;'
);
},
};
And for images (or other files where you only need a filename) from create react app:
'use strict';
const path = require('path');
const camelcase = require('camelcase');
// This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/en/webpack.html
module.exports = {
process(src, filename) {
const assetFilename = JSON.stringify(path.basename(filename));
if (filename.match(/\.svg$/)) {
// Based on how SVGR generates a component name:
// https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
const pascalCaseFilename = camelcase(path.parse(filename).name, {
pascalCase: true,
});
const componentName = `Svg${pascalCaseFilename}`;
return `const React = require('react');
module.exports = {
__esModule: true,
default: ${assetFilename},
ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
return {
$$typeof: Symbol.for('react.element'),
type: 'svg',
ref: ref,
key: null,
props: Object.assign({}, props, {
children: ${assetFilename}
})
};
}),
};`;
}
return `module.exports = ${assetFilename};`;
},
};

how to do cypress unit tests with vue/cli

I am using vue/cli 3 configured for cypress e2e tests. The e2e test scenario works fine but I also wish to use cypress for unit tests. I installed cypress-vue-unit-test but when loading a single component (using mountVue) cypress fails to interpret the Vue syntax ( etc).
I presume I have to add configuration so that the correct web pack loaders are used at the preprocessor stage when cypress bundles the files. I have been unable to figure out how to accomplish this as there is no web pack config file in my project and I am not sure how to modify the preconfigured set-up. Can anyone guide me?
Thanks phoet, you pointed me in the right direction. The solution was to place the configuration in tests/e2e/plugins/index.js with the following content (probably could be refined):
const webpack = require("#cypress/webpack-preprocessor");
const VueLoader = require("vue-loader/lib/plugin");
const webpack_vue_cypress_config = {
webpackOptions: {
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.css$/,
use: ["vue-style-loader", "css-loader"]
}
]
},
resolve: {
extensions: [".js", ".vue", ".json"],
alias: {
vue$: "vue/dist/vue.esm.js",
"#": "../../"
}
},
plugins: [new VueLoader()]
},
watchOptions: {}
};
module.exports = (on, config) => {
on("file:preprocessor", webpack(webpack_vue_cypress_config));
return Object.assign({}, config, {
fixturesFolder: "tests/e2e/fixtures",
integrationFolder: "tests/e2e/specs",
screenshotsFolder: "tests/e2e/screenshots",
videosFolder: "tests/e2e/videos",
supportFile: "tests/e2e/support/index.js"
});
};
Thanks Linus; that's much cleaner
const webpack = require("#cypress/webpack-preprocessor");
const options = {
webpackOptions: require("#vue/cli-service/webpack.config.js"),
watchOptions: {}
};
module.exports = (on, config) => {
on("file:preprocessor", webpack(options));
return Object.assign({}, config, {
fixturesFolder: "tests/e2e/fixtures",
integrationFolder: "tests/e2e/specs",
screenshotsFolder: "tests/e2e/screenshots",
videosFolder: "tests/e2e/videos",
supportFile: "tests/e2e/support/index.js"
});
};

mocking a require()'d imported object with jest/sinon

here's my code:
const Config = require('Config');
export const getPayments = (username: string) => {
if (username === undefined) {
throw new Error('username is undefined');
}
const envEndpoint = Config.paymentsEndpoint;
const endpoint: string = `${envEndpoint}/${username}`;
return fetch(endpoint);
};
What I want to mock is the Config object. I basically want to intercept that require call in my test file and replace it with a an object with that paymentsEndpoint value. I am using jest, but also have sinon as an option for mocking.
I am still fairly new to unit tests and jest, so forgive me if I used incorrect terminology
In your test file before you import your getPayments module, you can use jest.mock to mock the Config module.
So it would look something like
/* getPayments.test.js */
jest.mock('Config', () => {
return { paymentsEndpoint: 'y' }; // return your fake Config obj here
});
import getPayments from './getPayments'; // or whatever the correct file path is
describe(() => {
[ ... your tests ]
});
here are the jest.mock docs: https://facebook.github.io/jest/docs/en/jest-object.html#jestmockmodulename-factory-options