How to use imported graphql files in jest unit testing? - unit-testing

I'm trying to run unit tests with jest but I'm getting the following error:
● Test suite failed to run
/apollo/queries/articles.gql:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){query articles($orderBy: [OrderByClause!], $stripTags: Boolean, $maxCharacters: Int) {
^^^^^^^^
SyntaxError: Unexpected identifier
I have installed
https://github.com/jagi/jest-transform-graphql
It's suppose to transform GQL files.
My package.json (jest part)
"jest": {
"moduleFileExtensions": [
"js",
"json",
"vue",
"gql"
],
"watchman": false,
"moduleNameMapper": {
"^~/(.*)$": "<rootDir>/$1",
"^~~/(.*)$": "<rootDir>/$1"
},
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/babel-jest",
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
"\\.(gql|graphql)$": "#jagi/jest-transform-graphql"
},
"snapshotSerializers": [
"<rootDir>/node_modules/jest-serializer-vue"
],
"collectCoverage": true,
"collectCoverageFrom": [
"<rootDir>/components/**/*.vue",
"<rootDir>/pages/*.vue"
]
}
Test file
import Index from "../index";
const factory = () =>
shallowMount(Index, {
propsData: {
label: "click me!"
}
});
describe("Index", () => {
test("mounts properly", () => {
const wrapper = factory();
expect(wrapper.isVueInstance()).toBeTruthy();
});
test("renders properly", () => {
const wrapper = factory();
expect(wrapper.html()).toMatchSnapshot();
});
});
index.vue file (stripped out unimportant things)
<template>
<div></div>
</template>
<script lang="ts">
import Vue from "vue";
import ArticlesQuery from "~/apollo/queries/articles.gql";
export default Vue.extend({
name: "Homepage",
apollo: {
articles: {
query: ArticlesQuery,
variables() {
return {
orderBy: [{ field: "id", order: "DESC" }],
stripTags: true,
maxCharacters: 150
};
},
prefetch: true
}
}
});
</script>
This is my first time doing unit testing, so I have zero knowledge on this subject.

I had the same problem with Nuxt. I installed this dependence: https://www.npmjs.com/package/jest-transform-graphql, and add this: '\.(gql|graphql)$': 'jest-transform-graphql' in jest.config.js file, it works for me
transform: {
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest',
'\\.(gql|graphql)$': 'jest-transform-graphql'
},

Related

"ReferenceError: Worker is not defined when running tests in Jest"

I am using Jest to run tests for a React app, and I am seeing the following error when running the tests:
ReferenceError: Worker is not defined
This error is occurring even though the component that is being tested is not using the heic2any library. The error is causing the tests to stop running.
Here is the code for the component that is being tested:
import { Button } from '#material-ui/core';
import { ReactElement } from 'react';
import useBreakpoint from 'src/hooks/useBreakpoint';
interface ActionButtonProps {
isMobileOnly?: boolean;
icon?: ReactElement;
onClick?: () => void;
['data-testid']?: string
}
const ActionButton = ({ isMobileOnly = false, icon, onClick, ...rest }: ActionButtonProps) => {
const { isMobile } = useBreakpoint();
const text = isMobile ? 'Add' : 'Add Photo Description Set';
if ((!isMobile && isMobileOnly) || (isMobile && !isMobileOnly)) {
return null;
}
return (
<Button
data-testid={rest['data-testid']}
startIcon={icon}
variant="outlined"
onClick={onClick}
>
{text}
</Button>
);
};
export default ActionButton;
This is the code of the component where heic2any was imported (which is another file)
import React from 'react';
import { SxProps } from '#material-ui/system';
import { PencilAlt } from 'src/icons';
import heic2any from 'heic2any';
interface FileImageDropzoneProps extends DropzoneOptions {
cardMediaSx?: SxProps;
}
const FileImageDropzone: React.FC<FileImageDropzoneProps> = (props) => {
// component code goes here...
};
export default FileImageDropzone;
Here's my unit test:
import ActionButton from '../ActionButton';
import { render, screen, cleanup, fireEvent } from '#testing-library/react';
import useBreakpoint, { BreakpointHook } from 'src/hooks/useBreakpoint';
import '#testing-library/jest-dom';
afterEach(cleanup)
jest.mock('src/hooks/useBreakpoint');
const mockUseBreakpoint: jest.Mock<BreakpointHook> = useBreakpoint as jest.Mock<BreakpointHook>;
describe('ActionButton', () => {
it('should render the compnent', () => {
render(<ActionButton data-testid='action-button' />);
mockUseBreakpoint.mockReturnValue({
isMobile: true,
isDesktop: false,
})
const { getByTestId } = screen
expect(getByTestId('action-button')).toBeInTheDocument()
});
});
Here's my jest config :
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
isolatedModules: true,
},
],
},
testMatch: ['**/src/**/__tests__/**/*.test.ts?(x)'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
collectCoverage: true,
coveragePathIgnorePatterns: [
'/node_modules/',
'/__tests__/',
'/dist/',
'/coverage/',
],
setupFilesAfterEnv: [
'#testing-library/jest-dom/extend-expect',
'jest-canvas-mock',
'<rootDir>/jest.setup.ts',
],
moduleNameMapper: {
'^components/(.*)$': '<rootDir>/src/components/$1',
},
testPathIgnorePatterns: ['node_modules'],
moduleNameMapper: {
'^src/(.*)$': '<rootDir>/src',
},
moduleDirectories: ['node_modules'],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};
I am not sure why this error is occurring or how to fix it. Can anyone help me understand what is causing this issue and how to resolve it? The error even shows even if i commented out the it()

Rollup failed to resolve import "src/bootstrap.tsx" from "index.html"

The problem :
This is what happens when i run the build with my React/Vite app :
vite v2.9.6 building for production...
✓ 1 modules transformed.
ERROR [vite]: Rollup failed to resolve import "src/bootstrap.tsx" from "index.html".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
Expected result : A successful build.
In dev mode everything works fine.
What i tried :
Here a solution which proposes to add "/" to the path of the script in index.html, but it is already the case in my code and it does not work. I still tried without "/", just in case.
Some of my code :
in index.html:
<script type="module" src="/src/bootstrap.tsx"></script>
in vite.config.ts :
import { defineConfig, loadEnv } from 'vite';
import react from '#vitejs/plugin-react';
import path from 'path';
import { viteStaticCopy } from 'vite-plugin-static-copy';
import { viteCommonjs, esbuildCommonjs } from '#originjs/vite-plugin-commonjs';
import viteCompression from 'vite-plugin-compression';
import { createHtmlPlugin } from 'vite-plugin-html';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), 'VITE_');
const publicPath = '/';
const productName = 'My product name';
return {
define: {
PUBLIC_PATH: JSON.stringify(publicPath),
PRODUCT_NAME: JSON.stringify(productName),
},
plugins: [
react({
jsxRuntime: 'classic',
}),
viteStaticCopy({
flatten: true,
targets: [
{
src: 'node_modules/#client-ux/mylib/assets/fonts',
dest: './assets',
},
],
}),
viteCommonjs(),
viteCompression({
threshold: 1024 * 8, // 8 KB
deleteOriginFile: false,
filter: /\.(js|json|ttf|eot|woff|otf)$/i,
}),
createHtmlPlugin({
entry: 'src/bootstrap.tsx',
inject: {
data: {
title: `<title>${productName}</title>`,
},
},
}),
],
optimizeDeps: {
esbuildOptions: {
plugins: [esbuildCommonjs(['#client-ux/mylib-react', '#otherlib/front-common'])],
},
},
resolve: {
alias: {
'#src': path.resolve(__dirname, 'src'),
'#root': path.resolve(__dirname, './'),
'#public': path.resolve(__dirname, './public'),
'#cypress': path.resolve(__dirname, './cypress'),
'#dist': path.resolve(__dirname, './dist'),
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `
$mylib-fonts-path: ".${publicPath}${mode === 'development' ? 'assets/' : ''}";
#import "./src/shared/styles/variables";
#import "./src/shared/styles/mixins";
#import "./node_modules/#client-ux/mylib-core/src/app/styles/common/_default.scss";
`,
},
},
},
build: {
reportCompressedSize: false,
},
};
});

vue-test-utils does not resolve components

i've set it up in a nuxt project and when i run : npm start test it fails with the below error:
Cannot find module '#/components/Masonry' from 'components/RelatedPins/index.vue'
jest.config.js
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/$1',
'^~/(.*)$': '<rootDir>/$1',
'^vue$': 'vue/dist/vue.common.js',
},
moduleFileExtensions: ['ts', 'js', 'vue', 'json'],
transform: {
'^.+\\.ts$': 'ts-jest',
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest',
},
collectCoverage: true,
collectCoverageFrom: [
'<rootDir>/components/**/*.vue',
'<rootDir>/pages/**/*.vue',
],
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/test/mocks/fileMock.js',
'\\.(css|scss)$': '<rootDir>/test/mocks/styleMock.js',
},
}
RelatedPins.spec.js
import RelatedPins from '../components/RelatedPins'
jest.mock('axios', () => ({
get: Promise.resolve('value')
}))
describe('RelatedPins', () => {
it('mock api request', () => {
const wrapper = mount(RelatedPins, {
propsData: {
apiAddress: 'home_page/items',
httpMethod: 'post'
}
})
})
})
and this is how i import the component in my targeted testing component (RelatedPins.vue)
import Masonry from '#/components/Masonry'

Aurelia unit testing access component's viewModel

I am unit testing one of my components in an Aurelia project. I'd like to access my component's viewModel in my unit test but haven't had any luck so far.
I followed the example available at https://aurelia.io/docs/testing/components#manually-handling-lifecycle but I keep getting component.viewModel is undefined.
Here is the unit test:
describe.only('some basic tests', function() {
let component, user;
before(() => {
user = new User({ id: 100, first_name: "Bob", last_name: "Schmoe", email: 'joe#schmoe.com'});
user.save();
});
beforeEach( () => {
component = StageComponent
.withResources('modules/users/user')
.inView('<user></user>')
.boundTo( user );
});
it('check for ', () => {
return component.create(bootstrap)
.then(() => {
expect(2).to.equal(2);
return component.viewModel.activate({user: user});
});
});
it('can manually handle lifecycle', () => {
return component.manuallyHandleLifecycle().create(bootstrap)
.then(() => component.bind({user: user}))
.then(() => component.attached())
.then(() => component.unbind() )
.then(() => {
expect(component.viewModel.name).toBe(null);
return Promise.resolve(true);
});
});
afterEach( () => {
component.dispose();
});
});
Here is the error I get:
1) my aurelia tests
can manually handle lifecycle:
TypeError: Cannot read property 'name' of undefined
Here is the the line that defines the viewModel on the component object but only if aurelia.root.controllers.length is set. I am not sure how to set controllers in my aurelia code or if I need to do so at all.
I guess my question is:
How do I get access to a component's viewModel in my unit tests?
Edit #2:
I'd also like to point out that your own answer is essentially the same solution as the one I first proposed in the comments. It is the equivalent of directly instantiating your view model and not verifying whether the component is actually working.
Edit:
I tried this locally with a karma+webpack+mocha setup (as webpack is the popular choice nowadays) and there were a few caveats with getting this to work well. I'm not sure what the rest of your setup is, so I cannot tell you precisely where the error was (I could probably point this out if you told me more about your setup).
In any case, here's a working setup with karma+webpack+mocha that properly verifies the binding and rendering:
https://github.com/fkleuver/aurelia-karma-webpack-testing
The test code:
import './setup';
import { Greeter } from './../src/greeter';
import { bootstrap } from 'aurelia-bootstrapper';
import { StageComponent, ComponentTester } from 'aurelia-testing';
import { PLATFORM } from 'aurelia-framework';
import { assert } from 'chai';
describe('Greeter', () => {
let el: HTMLElement;
let tester: ComponentTester;
let sut: Greeter;
beforeEach(async () => {
tester = StageComponent
.withResources(PLATFORM.moduleName('greeter'))
.inView(`<greeter name.bind="name"></greeter>`)
.manuallyHandleLifecycle();
await tester.create(bootstrap);
el = <HTMLElement>tester.element;
sut = tester.viewModel;
});
it('binds correctly', async () => {
await tester.bind({ name: 'Bob' });
assert.equal(sut.name, 'Bob');
});
it('renders correctly', async () => {
await tester.bind({ name: 'Bob' });
await tester.attached();
assert.equal(el.innerText.trim(), 'Hello, Bob!');
});
});
greeter.html
<template>
Hello, ${name}!
</template>
greeter.ts
import { bindable } from 'aurelia-framework';
export class Greeter {
#bindable()
public name: string;
}
setup.ts
import 'aurelia-polyfills';
import 'aurelia-loader-webpack';
import { initialize } from 'aurelia-pal-browser';
initialize();
karma.conf.js
const { AureliaPlugin } = require('aurelia-webpack-plugin');
const { resolve } = require('path');
module.exports = function configure(config) {
const options = {
frameworks: ['source-map-support', 'mocha'],
files: ['test/**/*.ts'],
preprocessors: { ['test/**/*.ts']: ['webpack', 'sourcemap'] },
webpack: {
mode: 'development',
entry: { setup: './test/setup.ts' },
resolve: {
extensions: ['.ts', '.js'],
modules: [
resolve(__dirname, 'src'),
resolve(__dirname, 'node_modules')
]
},
devtool: 'inline-source-map',
module: {
rules: [{
test: /\.html$/i,
loader: 'html-loader'
}, {
test: /\.ts$/i,
loader: 'ts-loader',
exclude: /node_modules/
}]
},
plugins: [new AureliaPlugin()]
},
singleRun: false,
colors: true,
logLevel: config.browsers && config.browsers[0] === 'ChromeDebugging' ? config.LOG_DEBUG : config.LOG_INFO, // for troubleshooting mode
mime: { 'text/x-typescript': ['ts'] },
webpackMiddleware: { stats: 'errors-only' },
reporters: ['mocha'],
browsers: config.browsers || ['ChromeHeadless'],
customLaunchers: {
ChromeDebugging: {
base: 'Chrome',
flags: [ '--remote-debugging-port=9333' ]
}
}
};
config.set(options);
};
tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"importHelpers": true,
"lib": ["es2018", "dom"],
"module": "esnext",
"moduleResolution": "node",
"sourceMap": true,
"target": "es2018"
},
"include": ["src"]
}
package.json
{
"scripts": {
"test": "karma start --browsers=ChromeHeadless"
},
"dependencies": {
"aurelia-bootstrapper": "^2.3.0",
"aurelia-loader-webpack": "^2.2.1"
},
"devDependencies": {
"#types/chai": "^4.1.6",
"#types/mocha": "^5.2.5",
"#types/node": "^10.12.0",
"aurelia-testing": "^1.0.0",
"aurelia-webpack-plugin": "^3.0.0",
"chai": "^4.2.0",
"html-loader": "^0.5.5",
"karma": "^3.1.1",
"karma-chrome-launcher": "^2.2.0",
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.5",
"karma-source-map-support": "^1.3.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^3.0.5",
"mocha": "^5.2.0",
"path": "^0.12.7",
"ts-loader": "^5.2.2",
"typescript": "^3.1.3",
"webpack": "^4.23.1",
"webpack-dev-server": "^3.1.10"
}
}
Original answer
If you're manually doing the lifecycle, you need to pass in a ViewModel yourself that it can bind to :)
I don't remember exactly what's strictly speaking needed so I'm quite sure there's some redundancy (e.g. one of the two bindingContexts passed in shouldn't be necessary). But this is the general idea:
const view = "<div>${msg}</div>";
const bindingContext = { msg: "foo" };
StageComponent
.withResources(resources/*optional*/)
.inView(view)
.boundTo(bindingContext)
.manuallyHandleLifecycle()
.create(bootstrap)
.then(component => {
component.bind(bindingContext);
}
.then(component => {
component.attached();
}
.then(component => {
expect(component.host.textContent).toEqual("foo");
}
.then(component => {
bindingContext.msg = "bar";
}
.then(component => {
expect(component.host.textContent).toEqual("bar");
};
Needless to say, since you create the view model yourself (the variable bindingContext in this example), you can simply access the variable you declared.
In order to get it to work, I had to use Container:
import { UserCard } from '../../src/modules/users/user-card';
import { Container } from 'aurelia-dependency-injection';
describe.only('some basic tests', function() {
let component, user;
before(() => {
user = new User({ id: 100, first_name: "Bob", last_name: "Schmoe", email: 'joe#schmoe.com'});
user.save();
});
beforeEach(() => {
container = new Container();
userCard = container.get( UserCard );
component = StageComponent
.withResources('modules/users/user-card')
.inView('<user-card></user-card>')
.boundTo( user );
});
it('check for ', () => {
return component.create(bootstrap)
.then(() => {
expect(2).to.equal(2);
return userCard.activate({user: user});
});
});
});

How do I unit test a quasar app using Jest?

I have an quasar application that was generated with the quasar-cli.
How do I integrate a unit test into a test runner like Jest for an application like this?
I've added a this to my Jest configuration
"moduleNameMapper": {
"quasar": "<rootDir>/node_modules/quasar-framework"
}
Unfortunately, Jest reports back
Cannot find module 'quasar' from 'index.vue'
Here is the a snippet of the Vue file
<template>
<div style="padding-top: 20px" v-if="refund.type != null ">
<q-btn :label="'Issue ' + ( currency(refund.amount)) + ' Refund'" :disable="refund.amount <= 0" #click="issueRefund()" color="green" class="full-width" :loading="noteLoading" />
</div>
</template>
<script>
import { Notify } from "quasar"; // here is where I am using Quasar
issueRefund() {
this.noteLoading = true;
this.$axios
.post(`${BASE_URL}/issue_refund/?secret=${this.secret}`, {
refund: this.refund,
agent_email: this.userEmail,
order_id: this.selectedOrder.id,
agent_name: this.$route.query.user_name,
order_number: this.selectedOrder.order_number,
ticket_id: this.ticketId
})
.then(res => {
this.noteLoading = false;
if ((res.data.res === "success")) {
Notify.create({
position: "bottom",
type: "positive",
message: "Refund Issued."
});
this.selectedOrder = res.data.order;
this.resetRefundObj();
this.$refs.refundDiag.hide();
} else {
Notify.create({
position: "bottom",
type: "negative",
message: res.data.error
});
}
});
},
</script>
Integrating Jest with Quasar is quite straight-forward. You'll need two packages, babel-jest and jest.
yarn add jest babel-jest -D
After adding those two dependencies, create a jest.config.js file at the root of your project--here's where all the jest configuration goes.
Here's how the jest.config.js file should look like;
module.exports = {
globals: {
__DEV__: true,
},
verbose: false, // false since we want to see console.logs inside tests
bail: false,
testURL: 'http://localhost/',
testEnvironment: 'jsdom',
testRegex: './__unit__/.*.js$',
rootDir: '.',
testPathIgnorePatterns: [
'<rootDir>/components/coverage/',
'<rootDir>/test/cypress/',
'<rootDir>/test/coverage/',
'<rootDir>/dist/',
'<rootDir>/node_modules/',
],
moduleFileExtensions: ['js', 'json', 'vue'],
moduleNameMapper: {
'^vue$': 'vue/dist/vue.common.js',
'quasar': 'quasar-framework/dist/umd/quasar.mat.umd.js',
},
resolver: null,
transformIgnorePatterns: [
'node_modules/core-js',
'node_modules/babel-runtime',
'node_modules/vue',
],
transform: {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
}
}
Then create a folder inside the root of your project called __unit__
Place a file called MyUnitTest.test.js inside the __unit__ folder. Now Jest picks up files from this folder.
The final touch would be to run the tests, simply add this to the package.json
"unit": "yarn run jest --config jest.config.js"
Boom! -- Now you may run yarn run unit or yarn run unit --watch and it should work.
Here's a sample of a Quasar component and Jest test.
import { createLocalVue, shallowMount } from '#vue/test-utils'
import Vuex from 'vuex'
import Quasar, * as All from 'quasar'
import CookieConsent from '#components/common/CookieConsent.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Quasar, { components: All, directives: All, plugins: All })
describe('CookieConsent.vue', () => {
const wrapper = shallowMount(CookieConsent, {
localVue,
mocks: {
$t: () => {},
},
})
test('CookieConsent.vue mock should exist', () => {
expect(wrapper.exists()).toBe(true)
})
})
Hope you found this useful