I want to test a stencil component and configure a global variable in my test like this:
describe('my-component', () => {
const myVarMock = 1;
let page;
let shadowRoot: ShadowRoot;
beforeEach(async () => {
page = await newSpecPage({
components: [MyComponent],
html: `<my-component></my-component>`,
supportsShadowDom: true,
autoApplyChanges: true
});
shadowRoot = page.root.shadowRoot;
});
it('should test', () => {
page.rootInstance.myVar= myVarMock;
page.rootInstance.componentWillLoad();
page.rootInstance.render();
console.log(shadowRoot.innerHTML.toString());
const buttonElement = shadowRoot.querySelector('.my-button'); //is null because shadow root is empty
});
});
My Component only renders something, when myVar is set. In the console.log of my test, shadowRoot is always empty, although I explicitly call render() in the test and when I go through the render function in debug-mode it has a value for myVar and renders everything. But why is shadowRoot then empty and my buttonElement is undefined?
Component:
#Component({
tag: 'my-component',
shadow: true,
})
export class MyComponent{
public myVar;
componentWillLoad() {
...
}
render() {
return (
<Host>
{this.myVar? (
<div class="my-button"></div>
): null}
</Host>
)
}
}
Calling those life-cycle hooks like componentWillLoad and render manually does not do what I think you're expecting it to do. The Stencil runtime calls render and uses the return value (JSX) to eventually render your component. Manually calling render does not render or re-render your component. In fact, it doesn't do anything except returning some JSX to you but you're not doing anything with the return value.
I think the main issue in your case is that myVar is not declared as a property with the #Prop() decorator. So even though you have marked your class member as public and are able to change it from the outside, Stencil will not wire up anything for you regarding that prop. See https://stenciljs.com/docs/properties.
Instead, you'll have to define it as:
#Prop() myVar?: number;
That way it will cause Stencil to re-render your component every time you update the prop's value.
Your test case should just look like
it('should test', () => {
page.root.myVar = myVarMock;
console.log(shadowRoot.innerHTML.toString());
const buttonElement = shadowRoot.querySelector('.my-button');
expect(buttonElement).not.toBeNull();
});
Related
I am trying to make a simple test to check that the button is emitting an event called "click" every time it is detected, the problem is that when I use wrapper.emitted('click') to validate that it is receiving it, it always arrives as an empty object... I don't know what I may be doing wrong.
Current versions:
Vue: 3.2.31
Vitest: 0.7.12
Vite: 2.8.6
Vue/test-utils: 2.0.0-rc.17
<template>
<button
class="eci-button"
:disabled="props.disabled"
#click="handleClick"
>
{{ props.label }}
</button>
</template>
<script setup lang="ts">
/* Interfaces and types */
interface Props {
label: string
disabled?: boolean
}
/* Props */
const props = withDefaults(defineProps<Props>(), {
disabled: false
})
/* Events */
const emit =
defineEmits<{ (e: 'click'): void }>()
/* Methods */
const handleClick = () => {
emit('click')
}
</script>
<style lang="scss" src="./Button.scss"></style>
Test
test('should render and emit event at click', async () => {
const label = 'Siguiente'
const wrapper = mount(Button, {
props: {
label
}
})
wrapper.trigger('click')
expect(wrapper.emitted()).toHaveProperty('click')
})
Result
OK, I found the problem... I am using happy-dom as test environment together with Vitest... the problem is that when you pass a property as "optional" for some reason it interprets it as if it was "true"... to explain myself...
I have a default prop "disabled" set to "false" which means that my button should print as:
in the DOM.. but, for some reason it interprets it as being true and that causes the "click" events not being executed.. i will keep looking for some fix for this, at the moment i have migrated for jsdom and solved.
I'm using tiptap and trying to extend the Paragraph node to wrap some extra stuff around its view. I used <NodeViewWrapper> and <NodeViewContent> as the guides said.
const ParagraphWrapper = () => {
return (
<NodeViewWrapper>
<NodeViewContent />
</NodeViewWrapper>
)
}
const ParagraphExt = Paragraph.extend({
addNodeView() {
return ReactNodeViewRenderer(ParagraphWrapper)
}
})
export default function App() {
const editor = useEditor({
extensions: [
Document,
Text,
ParagraphExt, // <<<< text-align was not rendered
// Paragraph, // <<<< This worked
TextAlign.configure({
types: ["paragraph"]
}),
],
content: `<p style="text-align: center">This is a paragraph</p>`,
})
return (
<>
<EditorContent editor={editor} />
<pre>{JSON.stringify(editor?.getJSON?.(), null, 2)}</pre>
</>
);
}
However, this seems to render the node from scratch. Thus, other extensions, such as textAlign no longer works.
I only need to wrap a thin layer around whatever was rendered originally. How do I do that?
Code Sandbox
You still get access to the attrs being passed to the node which is available in props. You can use that info to style your rendered content as you wish.
const ParagraphWrapper = (props) => {
const textAlign = props.node.attrs.textAlign;
return (
<NodeViewWrapper>
<NodeViewContent style={{textAlign}} />
</NodeViewWrapper>
);
};
I am trying to pass a function as property inside a component in Vue3. Here is the code:
// ExampleComponent.vue
<template>
Here goes component content...
</template>
<script>
export default {
name: ''
}
</script>
<script setup>
import { onMounted } from "vue"
const props = defineProps({
onLoad: {
type: Function,
default() {
return {}
}
}
})
onMounted(() => {
if (props.onLoad) { // This doesn't work and passes every time
props.onLoad()
}
})
</script>
Here is the parent component calling child ExampleComponent.vue. It may or may not pass the onLoad function.
<ExampleComponent \>
What I want is that call this function only if the property on-load is passed? Is there a way in Vue3 to check if the property is passed explicitly?
you can use watcheffect :
javascript
watchEffect(() => {
if(props.onLoad) props.onLoad()
});
once '''onLoad''' value changed, it will tigger.
i am trying to clear my Textinput focus inside keyboard hide event,but i am not able to get reference
inside keyboard hide event method.i tried to print props value it also getting undefined
constructor (props) {
this.inputs = {};
}
_keyboardDidHide () {
console.log("value"+this.props);
this.inputs['inputValue'].blur();
}
componentWillMount () {
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
}
componentWillUnmount () {
this.keyboardDidHideListener.remove();
}
<TextInput
ref={input => {
this.inputs['inputValue'] = input;
}}
autoFocus={true}
blurOnSubmit={false}
/>
let me know how to clear the TextInput focus on _keyboardDidHide method.
I'm not 100% sure what you're trying to do here, however I assume you want to at least get the info out from your input.
No need for that ref magic there though, just use simple react state changes.
class InputWrapper extends React.Component {
constructor() {
super();
this.state = {
input: ''
};
this.handleInput = this.handleInput.bind(this);
}
handleInput(input) {
this.setState({input});
}
render() {
return (
<TextInput onChangeText={this.handleInput} />
);
}
}
This will give you a TextInput Component with control over the input.
Now you should add a componentDidUpdate method as well, that prints out the current state, so you can observe what is happening when you change the input value.
componentDidUpdate() {
console.log(this.state);
}
As for bluring and such, you should definitely check out the documentation on TextInput : https://facebook.github.io/react-native/docs/textinput.html
Additionally, might I suggest to jump into the lifecycle documentation of react itself, plus checking up on props vs state in react. It is a confusing concept in the beginning and you should definitely revisit it.
As for blurring the input, simply do this:
<TextInput
ref={input => this.input = input}
/>
And then you can call:
this.input.blur();
wherever you want.
Also, do not forget to bind your _keyboardDidHide callback within your constructor or when adding it as the listener callback, like so
this._keyboardDidHide = this._keyboardDidHide.bind(this)
Keyboard.addListener('keyboardDidHide', this._keyboardDidHide.bind(this));
Hope this helps
I'm using a helper method to render a template, like so:
module.exports = (Handlebars) => {
Handlebars.registerHelper('foo', () => {
var template = require('../path/to/template.hbs')
return template;
})
}
In the rendered template, I'd like to call another helper, bar.
// template.hbs
<div>
{{foo}}
</div>
// bar.js
module.exports = (Handlebars) => {
Handlebars.registerHelper('bar', ()=> {
// do something here
}
}
But the rendered template seems to have no visibility of the helper, i.e. bar isn't called. What's a good way to get around this?