I'm trying to test my tooltip component, but it seems it does not exist :cry:
My .html
<div>
<boxComponent>
Some text
<tooltipComponent
#mouseover.native="handleHover(true)"
#mouseleave.native="handleHover(false)"
>This text appears on Hover</tooltipComponent>
</boxComponent>
<switchComponent button-type="button" :label="false" #change="activeFun" />
</div>
My .js
methods: {
handleHover (s) {
this.onHoverTooltip = s
},
}
My .spec.js
const localVue = createLocalVue()
localVue.use(Vuex)
//...
it('should reveal tooltip\'s mesage', () => {
const wrapper = shallowMount(ozFilters, {
propsData: {
//others stuffs,
label: false,
},
localVue,
store,
stubs: ['tooltipComponent', 'boxComponent', 'switchComponent'],
})
expect(wrapper.find('tooltipComponent-stub').exists()).toBeFalsy()
// wrapper.vm.label = true
wrapper.vm.handleHover(true)
expect(wrapper.find('tooltipComponent-stub').exists()).toBeTruthy()
})
I need to understand what should I do to test the tooltip component that is already a custom component.
Even without the -stub it does not work.
The error is occurring in this line expect(wrapper.find('tooltipComponent-stub').exists()).toBeTruthy() with says that the expect is false.
Well, there are a couple of things that need to be fixed/clarified.
First of all, you are using shallowMount to create a component which you want to test, it stubs all custom components provided in tested component so you don't have to additionally use stub parameter to stub them, you can easily delete this: stubs: ['tooltipComponent', 'boxComponent', 'switchComponent'].
When you want to find specific component it's recommended to use findComponent instead of just find. (Using find to search for a component is actually deprecated). To properly identify component it's best to import it in your test file and use it in findComponent argument, something like this:
import BoxComponent from 'path/to/BoxComponent'
it('lovely test', () => {
wrapper.findComponent(BoxComponent)
})
After this your test should pass, but there are some things to consider like using mount instead of shallowMount. It's explained here, but to wrap it up, mount will render actual component with its children, where shallowMount stubs components inside tested component, so you are not testing the actual component but some "shadow" of its true nature. To see a difference between this functions I would recommend to create a wrapper with both functions and then see what html() will return from them.
Related
I'm trying to create a Vue 3 component library using composition API:
https://github.com/hyperbotauthor/vue3complib
In one of the components I would like to import an other composition API component ( https://github.com/hyperbotauthor/vue3complib/blob/main/src/components/ChessboardExt.vue ):
import { Perscombo } from "../index"
const PerscomboE = (Perscombo as any).setup
const e = PerscomboE({id: "variant", options: variants}, context)()
const vertContainer = h(
"div",
{
},
[e, outerContainer]
);
This almost works, because the component's node is created with its setup function, and it is even rendered on the page correctly, however its onMounted function does not get called properly and I get the warning
onMounted is called when there is no active component instance to be associated with.
Lifecycle injection APIs can only be used during execution of setup().
If you are using async setup(), make sure to register lifecycle hooks before the first await statement.
Not only a warning, but unfortunately I need this for initializing the component, so it is not fully functional without its onMounted function as it should be persistent and its state cannot be initialized from localStorage.
How do I import an other composition API component into my composition API component's setup properly?
EDIT:
Managed to remove onMounted from the child component and I can pass a callback in props for the case when its state changes. So for this case I solved the issue. In general I still don't know the solution.
You can pass an imported custom component via the h function, like an HTML tag. Event handling works the same way as with HTML elements: You prefix the handler name with on, use camel case, and pass the handler as a property with this name:
const variantCombo = h(Perscombo, {id: props.id + "/variant", options: variants, onPerscombochanged: (event:any) => {
setVariant()
}})
const sizeCombo = h(Perscombo, {id: props.id + "/size", options: sizes, onPerscombochanged: (event:any) => {
resize(event.value)
}})
const upperControls = h(
"div",
{
},
[variantCombo, sizeCombo]
);
welcome to this topic. i recently tried to use the Nuxt framework to make my web-application but i ran into a problem.
In my default layout i have two components. a header component and a sidebar component. if i click on the hamburger icon in the header component the sidebar needs to get smaller or bigger depending on the hamburger icon state (true or false)
so to make it more complicated i don't want to use a prop to send it through the other component. i want to make it as a template so people can use it easy. can i transform a local component variable to a global variable other components can use?
so the code i have now is like this:
this is the index page
this is the header component
this is the sidebar component
as you can see i trigger the hamburgerstate on the header component page.
i want to access that state in the sidebarcomponent to so i can adjust the sidebar
the one thing that's IMPORTANT is that it needs to be as simple as possible so people who use this template later don't have to add unnecessary work
any possibilities this can work?
The simplest way to achieve a global variable is to set it as a state element and have a mutation for changing it. As your 'hambuger' is a boolean there is no need to pass parameters to the mutation making it all the easier.
You may want to have a named module in you store to handle this but I'll just put it in store/index.js for now.
export const state = () => ({
hamburger: true
})
export const mutations = {
changeHamburger (state) {
state.hamburger = !state.hamburger
}
}
Then in any page or component you can access that state element:
Component.vue
<script>
import { mapMutations } from 'vuex'
export default {
computed: {
hamburger () {
return this.$store.state.hamburger
}
},
methods: {
...mapMutations({
hamburgerChange: 'changeHamburger'
})
}
}
</script>
So this means you can now use the computed property 'hamburger' in your component and can change it by calling 'hamburgerChange', eg <v-btn #click="hamburgerChange">.
I'm a beginner, this is probably more of a javascript problem than vue but anyway:
there a plugin for spreadsheet named handsontable and in the normal use you make the table by doing this
hot = new Handsontable(container, {option})
and then you can use the method like hot.loadData() etc..
To use handsontable with vuejs, there a wrapper we can find here https://github.com/handsontable/vue-handsontable-official. With the wrapper you make a table like this :
<template>
<div id="hot-preview">
<HotTable :root="root" :settings="hotSettings"></HotTable>
</div>
</template>
<script>
import HotTable from 'vue-handsontable-official';
import Vue from 'vue';
export default {
data: function() {
return {
root: 'test-hot',
hotSettings: {
data: [['sample', 'data']],
colHeaders: true
}
};
},
components: {
HotTable
}
mounted () {
localforage.config({
driver: localforage.INDEXEDDB,
name: 'matchlist-database'
})
localforage.getItem('DB').then(function (value) {
console.log('then i fetch the DB: ' + JSON.stringify(value))
if (value !== 'null') {
console.log('dB contain something')
**root**.loadData(value)
}
</script>
So it work fine when i give an array but to load the data from a DB you must call the handsontable method hot.loadData(data).
i cannot find how to call this method in vuejs i always get the error
TypeError: root.loadData is not a function
i tried with all i could think of instead of root ex: HotTable.loadData(value)
but to no avail
Can someone point me out how i would call handsontable methods from the vuejs wrapper. Or point me out what kind of reading i should do to understand my mistake. Thank a lot
There are two problems here, not bad ones :)
1st problem:
If you want to refer to your data inside Vue's methods/computed properties/watchers/lifecycle events, you should use the this keyword. If you have data: function() { return { root: "root-value" }} and you would like to console.log that "root-value" string, you should write console.log(this.root) inside your mounted handler.
If you had something like:
data: function() {
return {
hot = new Handsontable(container, {option})
....
};
You could call hot.loadData() like so:
mounted() {
this.hot.loadData();
...
}
So this refers to the Vue instance which exposes your data properties.
2nd problem:
If I understand the component wrapper correctly, you are supposed to pass data to it as props, not call any Handsontable methods directly.
<HotTable :root="root" :settings="hotSettings"></HotTable>
This means that Vue passes whatever you have as root in your data to the HotTable component. It also passes whatever you have as settings in your data. In the example, HotTable receives these:
root: 'test-hot',
hotSettings: {
data: [['sample', 'data']],
colHeaders: true
}
Now if you want to change/update/modify/add data that should be passed to the HotTable component, you should update your data in the Vue instance. You should do something like this.hotSettings = something new and this.root = something else and the HotTable component would receive those.
To understand what's really happnening with the HotTable, read all of the component documentation. Really. You will save lots of time if you read through the documentation. It all makes sense after that!
https://v2.vuejs.org/v2/guide/components.html
I'm wanting to assert that a component gets called from within another component with the correct arguments.
So within the component that I am testing there is a Title component that gets called with properties title & url. I'm trying to assert that it gets called with the correct arguments.
I'm pretty sure I want to use a sinon spy and do something like this
const titleSpy = sinon.spy(Title, render)
expect(titleSpy).to.be.calledWith( '< some title >' )
but with regards to React and Enzyme, I'm not really sure what I should be spying on. (Because apparently it's not render!)
In my spec file I am importing Title and console.loging it's value to find a function to spy on and I get:
function _class() {
_classCallCheck(this, _class);
return _possibleConstructorReturn(this, Object.getPrototypeOf(_class).apply(this, arguments));
}
Any ideas on how I can do this? Is it a case of going through and finding the element and checking it's attributes? If so that seems a bit...messy and seems like it goes against the principle of the Shallow render ("Shallow rendering is useful to constrain yourself to testing a component as a unit").
If you're just checking the value of properties passed to the component, you don't need sinon. For example, given the following component:
export default class MyComponent extends React.Component {
render() {
return (
<MyComponent myProp={this.props.myProp} />)
}
}
Your test might look like this:
describe('MyComponent ->', () => {
const props = {
myProp: 'myProp'
}
it('should set myProp from props', () => {
const component = shallow(<MyComponent {...props} />)
expect(component.props().myProp).to.equal(props.myProp)
})
})
You can achieve it with the help of .contains() method, without messing up with spies.
If you have a component:
<Foo>
<Title title="A title" url="http://google.com" />
</Foo>
You can make such an assertion:
const wrapper = shallow(<Foo />);
expect(wrapper.contains(<Title title="A title" url="http://google.com" />)).to.equal(true);
Such will fail:
const wrapper = shallow(<Foo />);
expect(wrapper.contains(<Title title="A wrong title" url="http://youtube.com" />)).to.equal(true);
This is an older question, but my approach is a little different than the existing answers:
So within the component that I am testing there is a Title component that gets called with properties title & url. I'm trying to assert that it gets called with the correct arguments.
ie. You're wanting to check that the component being tested renders another component, and passes the correct prop(s) to it.
So if the component being tested looks something like:
const MyComp = ({ title, url }) => (
<div>
<Title title={title} url={url} />
</div>
)
Then the test could look something like:
import Title from 'path/to/Title';, u
it('renders Title correctly', () => {
const testTitle = 'Test title';
const testUrl = 'http://example.com';
const sut = enzyme.shallow(<MyComp title={testTitle} url={testUrl} />);
// Check tested component rendered
expect(sut.exists).toBeTruthy();
// Find the Title component in the subtree
const titleComp = sut.find(Title); // or use a css-style selector string instead of the Title import
// Check that we found exactly one Title component
expect(titleComp).toHaveLength(1);
// Check that the props that were passed were our test values
expect(titleComp.prop('title')).toBe(testTitle);
expect(titleComp.prop('url')).toBe(testUrl);
});
I generally find Enzyme's functions to be very useful for all kinds of checks about components, without needing other libraries. Creating Sinon mocks can be useful to pass as props to components, to (for example) test that a callback prop is called when a button is clicked.
prior to posting this question, I tried to search in sqa stackexchange but I found no post about shallow and render there, so I hope someone can help me out here.
When should I use shallow and render in testing react components?
Based on the airbnb docs, I've made some opinions on the difference of the two:
Since shallow is testing components as a unit, so it should be used for 'parent' components. (ex. Tables, Wrappers, etc.)
Render is for child components.
The reason I asked this question, is that I'm having a hard time to figure out which one I should use (though the docs say that they're very similar)
So, how do I know which one to use in a specific scenario?
As per the Enzyme docs:
mount(<Component />) for Full DOM rendering is ideal for use cases where you have components that may interact with DOM apis, or may require the full lifecycle in order to fully test the component (ie, componentDidMount etc.)
vs.
shallow(<Component />) for Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren't indirectly asserting on behavior of child components.
vs.
render which is used to render react components to static HTML and analyze the resulting HTML structure.
You can still see the underlying "nodes" in a shallow render, so for example, you can do something like this (slightly contrived) example using AVA as the spec runner:
let wrapper = shallow(<TagBox />);
const props = {
toggleValue: sinon.spy()
};
test('it should render two top level nodes', t => {
t.is(wrapper.children().length, 2);
});
test('it should safely set all props and still render two nodes', t => {
wrapper.setProps({...props});
t.is(wrapper.children().length, 2);
});
test('it should call toggleValue when an x class is clicked', t => {
wrapper.setProps({...props});
wrapper.find('.x').last().simulate('click');
t.true(props.toggleValue.calledWith(3));
});
Notice that rendering, setting props and finding selectors and even synthetic events are all supported by shallow rendering, so most times you can just use that.
But, you won't be able to get the full lifecycle of the component, so if you expect things to happen in componentDidMount, you should use mount(<Component />);
This test uses Sinon to spy on the component's componentDidMount
test.only('mount calls componentDidMount', t => {
class Test extends Component {
constructor (props) {
super(props);
}
componentDidMount() {
console.log('componentDidMount!');
}
render () {
return (
<div />
);
}
};
const componentDidMount = sinon.spy(Test.prototype, 'componentDidMount');
const wrapper = mount(<Test />);
t.true(componentDidMount.calledOnce);
componentDidMount.restore();
});
The above will not pass with shallow rendering or render
render will provide you with the html only, so you can still do stuff like this:
test.only('render works', t => {
// insert Test component here...
const rendered = render(<Test />);
const len = rendered.find('div').length;
t.is(len, 1);
});
The difference between shallow() and mount() is that
shallow() tests components in isolation from the child components they render while mount()goes deeper and tests a component's children.
For shallow() this means that if the parent component renders another component that fails to render, then a shallow() rendering on the parent will still pass.