cypress-svelte-unit-test icon indicating copy to clipboard operation
cypress-svelte-unit-test copied to clipboard

How to test component slots?

Open alejandroiglesias opened this issue 4 years ago • 8 comments

Hi, I cannot seem to find a way to test component slots. The examples in the readme seem outdated since I cannot seem to make them work. Can you please give an updated example? I appreciate your help in advance. Best regards.

alejandroiglesias avatar Jan 13 '21 07:01 alejandroiglesias

Hi, I just stumbled upon the same problem. A little investigation revealed that this was made possible with this PR: https://github.com/bahmutov/cypress-svelte-unit-test/pull/33

but was removed later ... at least it's gone by the update to svelte3: https://github.com/bahmutov/cypress-svelte-unit-test/pull/41

You (and I) probably could work around the problem by creating a test specific intermediary component, that wraps your component under test and provides the slots ... not nice but it should work.

Crenshinibon avatar Jan 13 '21 21:01 Crenshinibon

I found a solution to test slots on this Svelte issue comment. The idea behind it is to add a createSlots helper function as specified in such comment and use it to populate the $$slots prop of the component. I put such helper in cypress/helpers.js as an exported function:

export function createSlots(slots) {
  // ...
}

And then import it and use in the spec:

// Import helper function in spec:
// import { createSlots } from '../../../cypress/helpers'

mount(
  Content,
  {
    props: {
      $$slots: createSlots({ default: document.createTextNode('Content slot') }),
      $$scope: {}
    }
  }
)

In this case, I pass a text node with the content Content slot, but obviously you can pass any element. I don't know why the $$scope prop is required but it doesn't work without it. A bit hacky solution but it works just fine. Hopefully, we will have a cleaner solution in the future.

alejandroiglesias avatar Jan 20 '21 07:01 alejandroiglesias

Now the challenge I'm facing is how to pass another component into the slot? For example, to test components that are meant to be used as:

<Dropdown>
  <DropdownTrigger />
  <DropdownList />
</Dropdown>

Where the Dropdown component just renders what's passed into the default slot, but then it also creates context, and child components interact with such context, so no possibility to fully test the whole behavior when testing them in isolation. Any suggestions?

alejandroiglesias avatar Jan 27 '21 07:01 alejandroiglesias

Would svelte:component be what you're looking for? https://svelte.dev/docs#svelte_component

Can pass a comonent type in and render and instance of it.

JohnnyFun avatar Jan 27 '21 16:01 JohnnyFun

@JohnnyFun how do you pass a svelte:component to the mount function?

alejandroiglesias avatar Feb 06 '21 04:02 alejandroiglesias

Oh sorry, I misunderstood what you were asking. The way I've been doing it is by wrapping the component(s) I want to test in a wrapper components with some example usages. Then I simply mount that wrapper component instead of the underlying component. I put my "wrapper" components in "./cypress/fixtures/components".

Something like this:

<h3>Simple example</h3>
<MyListComponent {items} let:item dataTest="simple-example">
   <strong>{item.name}</strong>
   <em>{item.desc}</em>
</MyListComponent>

<h3>Some other example</h3>
<MyListComponent {items} let:item dataTest="other-example">
   ...
</MyListComponent>

<script>
   export let items
</script>

JohnnyFun avatar Feb 06 '21 18:02 JohnnyFun

Makes it really nice when building up a component too. Kind of like an interactive style guide that shows how to use your components and what the expected behavior of them is.

JohnnyFun avatar Feb 06 '21 18:02 JohnnyFun

I see what you mean. I ended up doing that in some cases since the team already has wrapper components for rendering in Storybook.

alejandroiglesias avatar Mar 30 '21 04:03 alejandroiglesias