bahmutov / cypress-svelte-unit-test

Unit testing Svelte components in Cypress E2E test runner
162 stars 21 forks source link

How to test component slots? #251

Open alejandroiglesias opened 3 years ago

alejandroiglesias commented 3 years ago

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.

Crenshinibon commented 3 years ago

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.

alejandroiglesias commented 3 years ago

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 commented 3 years ago

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?

JohnnyFun commented 3 years ago

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.

alejandroiglesias commented 3 years ago

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

JohnnyFun commented 3 years ago

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 commented 3 years ago

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.

alejandroiglesias commented 3 years ago

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