3

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>
        )
    }
}
maidi
  • 3,219
  • 6
  • 27
  • 55

1 Answers1

2

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();
});
Simon Hänisch
  • 4,740
  • 2
  • 30
  • 42
  • Thanks for the additional information. It sounds logically and I changed it to `@Prop` and removed the function calls, but buttonElement is still null. I also tried `@State` - same effect. – maidi Aug 12 '20 at 12:40
  • are you doing anything to `myVar` in `componentWillLoad()`? – Simon Hänisch Aug 13 '20 at 11:04