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()
Related
I'm trying to test a React component that uses Apollo userQuery but I'm getting this error:
console.error node_modules/react/cjs/react.development.js:209
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite
components) but got: undefined. You likely forgot to export your
component from the file it's defined in, or you might have mixed up
default and named imports.
Here is my test
import { MockedProvider } from '#apollo/client/testing'
import React from 'react'
import { render } from '#testing-library/react'
import { customerList } from '../../mocks'
import { ListCustomers } from '../../queries'
import CustomerListContainer from '../CustomerListContainer'
import '#testing-library/jest-dom'
const mocks = [
{
request: {
query: ListCustomers,
},
result: {
data: customerList,
},
},
]
describe('components: <CustomerListContainer />', () => {
it('renders customer list', () => {
render(
<MockedProvider mocks={mocks} addTypename={false}>
<CustomerListContainer customerRole="ADMIN" />
</MockedProvider>
)
})
})
Here is the component I'm testing:
import React from 'react'
import { useQuery } from '#apollo/client'
import { CustomerList } from '../components'
import { ListCustomers } from '../queries'
const CustomerListContainer = ({ customerRole }: { customerRole: string }) => {
const { data, loading, error } = useQuery(ListCustomers, {
variables: {
filter: {
role: {
eq: customerRole,
},
},
},
})
if (error) return <h1>Something went wrong.</h1>
if (loading) return <h1>Loading...</h1>
return (
<CustomerList customers={data.listCustomers.items} />
)
}
export default CustomerListContainer
From my package.json:
"#apollo/client": "^3.7.1",
"#testing-library/jest-dom": "^5.16.5",
"#testing-library/react": "^13.4.0",
"#testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
For context I used Create React App.
I needed to upgrade to the latest version of react-scripts, in my case version 5.0.1.
I try to run unit tests on vue components, the compnents are written with #vue/composition-api package and they also use vuetify.
The Application runs like expected, but when I run the tests I don't have access to the breakpoint property under context.root.$vuetify. When I print the context.root.$vuetify a see the vue component instance.
The error is "Cannot read property 'mdAndDown' of undefined" when i try to access it like that:
context.root.$vuetify.breakpoint.mdAndDown
This is is my jest config file:
module.exports = {
preset: '#vue/cli-plugin-unit-jest/presets/typescript-and-babel',
transform: { '^.*\\.js$': 'babel-jest' },
transformIgnorePatterns: ['node_modules/(?!vue-router|#babel|vuetify)'],
setupFiles: [
"<rootDir>/tests/unit/setup-env.ts",
],
};
This is the Component file:
<template>
<v-app
class="sst-app-container"
>
<cmp-loading
v-show="!loadedBasic"
/>
<div
id="app-wrp"
v-show="loadedBasic"
>
<cmp-side-bar />
<v-content
class="fill-height"
>
<router-view></router-view>
</v-content>
</div>
</v-app>
</template>
<script lang="ts">
import {
computed, createComponent, onMounted, reactive, toRefs, watch,
} from '#vue/composition-api';
import basicSetup from '#/modules/core/composables/basic-setup';
import initMd from '#/modules/core/devices/mouse/composables/init-md';
import store from '#/stores';
import cmpSideBar from '../components/sidebar/CoreSideBar.mouse.vue';
import cmpLoading from '../components/loading/CoreLoading.vue';
export default createComponent({
components: {
cmpLoading,
cmpSideBar,
},
setup(props, context) {
console.log(context.root.$vuetify)
const basics = basicSetup();
return {
...basics,
};
},
});
</script>
This is my test:
import Vue from 'vue';
import Vuetify from 'vuetify';
import BtnCmp from '../../../../../components/vc-btn.vue';
import CmApi from '#vue/composition-api';
import { library } from '#fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome';
import {
faPlug,
faSignOut,
faCog,
faUser,
faTachometer,
faArrowLeft,
faArrowRight,
} from '#fortawesome/pro-duotone-svg-icons';
library.add(
faPlug,
faSignOut,
faCog,
faUser,
faTachometer,
faArrowLeft,
faArrowRight,
);
import { Breakpoint, Theme, Application, Goto, Icons, Lang, Presets } from 'vuetify/lib/services';
import {
mount,
createLocalVue,
} from '#vue/test-utils';
import Core from '../views/Core.mouse.vue';
const vue = createLocalVue();
vue.component('font-awesome-icon', FontAwesomeIcon);
vue.component('vc-btn', BtnCmp);
vue.use(Vuetify);
vue.use(CmApi);
describe('Core', () => {
let vuetify;
beforeEach(() => {
vuetify = new Vuetify({
mocks: {
breakpoint: new Breakpoint({
breakpoint: {
scrollBarWidth: 0,
thresholds: {
xs: 0,
sm: 0,
md: 0,
lg: 0,
},
},
},
});
});
it('renders the correct markup', async () => {
// Now mount the component and you have the wrapper
const wrapper = mount(Core, {
localVue: vue,
vuetify,
stubs: ['router-link', 'router-view'],
mocks: {
$t: () => 'some specific text'
},
});
expect(wrapper.html()).toContain('....');
});
});
This can be solved by adding a new instance of vuetify to the wrapper vuetify: new Vuetify()
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'
},
I am writing unit test cases for a Vue.js component.
When I am trying to shallowMount component using vue-test-util I get an error:
TypeError: $ is not a function at VueComponent.mounted
(src/components/gridComponent/index.vue:7579:7)
My code:
import $ from 'jquery';
global['$'] = global['jQuery'] = $;
import { shallowMount, createLocalVue } from '#vue/test-utils'
import gridComponent from '#/components/gridComponent/index'
describe('grid view component', () => {
it('has the expected HTML structure', async () => {
let wrapper = await shallowMount(gridComponent, {
stubs: ['GridLayout', 'GridItem'],
propsData: {componentsList}
})
expect(wrapper.element).toMatchSnapshot()
})
})
Below is the code of gridComponent:
import * as $ from 'jquery'
export default {
name: 'gridComponent',
components: { GridLayout, GridItem, DxpCommonModal },
props: ['componentsList'],
watch: {
componentsList: function (newVal, oldVal) {
// eslint-disable-next-line
this.matchingComponents = this.componentsList
}
},
data () {
return {
isDraggable: true,
isResizable: true
}
},
created () {
},
computed: {
...mapGetters(['getResources'])
},
mounted () {
$('#headerComponent').html(this.getSelectorHTML(this.siteDetails.header))
$('#footerComponent').html(this.getSelectorHTML(this.siteDetails.footer))
this.addHtmlOfComponentsInAPage()
},
destroyed () {
},
methods: {
addHtmlOfComponentsInAPage () {
this.selectedPage.Components.forEach((element, index) => {
$('#${index}').html(this.getSelectorHTML(this.selectedPage.Components[index]))
})
},
getSelectorHTML (component) {
const element = document.createElement(component.componentType)
element.setAttribute('content-id', new Date().getTime().toString())
if (!this.values && component.demoData) {
this.values = component.demoData
}
if (this.values) {
this.createMarkup(element, this.values)
this.values = ''
}
return element
}
}
}
Troubleshooting
In the component, try
const $ = require('jquery')
Put this before the usage in the method causing the error.
This will let you know if you are un-defining or redefining it somewhere.
Solution 2
You may need jsdom.
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const { window } = new JSDOM(`<!DOCTYPE html>`);
const $ = require('jquery')(window);
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});
});
});
});