Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Svelte extend not triggered when using render #419

Closed
EricVanEldik opened this issue Dec 30, 2024 · 3 comments
Closed

Svelte extend not triggered when using render #419

EricVanEldik opened this issue Dec 30, 2024 · 3 comments

Comments

@EricVanEldik
Copy link

I'm having an issue when trying to test custom elements (using jsdom) in Svelte.
When I use const result = render(MyComponent, { props: { options: '["v1","v2","v3"]', value: '', multiple: false } }); the component get rendered and all functions work as it should.

However the extend in this part of the code is never executed:

<svelte:options
  customElement={{
    tag: 'atp-select',
    shadow: 'none',
    extend: customElementConstructor => {
      return class extends customElementConstructor {
        constructor() {
          console.log('customElementConstructor called');
          super();
          this.self = this;
        }
      };
    },
  }}
/>

How can I make sure the code int the extend is executed when testing?

@timdeschryver timdeschryver transferred this issue from testing-library/dom-testing-library Dec 30, 2024
@mcous
Copy link
Collaborator

mcous commented Dec 30, 2024

I don't work with compiling Svelte to custom elements, but I wouldn't expect any custom element code (like extends) to run if you pass the component the Svelte's mount function, which is what render does. You'll likely want to avoid using @testing-library/svelte at all.

Instead, you probably want to compile your components and use the DOM directly:

import { test } from 'vitest'
import { getByRole } from '@testing-library/dom'

import 'my-custom-component-library'

test('my custom component', () => {
  const subject = document.createElement('atp-select')
  
  // ...
})

Let me know if you get this working, since it might be useful to throw into the docs somewhere

@EricVanEldik
Copy link
Author

EricVanEldik commented Dec 31, 2024

Yes, you are correct, loading the element with document.createElement does call the extend and makes the element available to test.
I had to add the element to the document.body and add an delay in order to trigger the render of the element, so my final code looks like this:

import { expect, test, vi } from 'vitest';
import './Select.svelte'; // my custom element, this needs to only load

// Svelte throws an animate error when running in vitest, this catches this error
Element.prototype.animate = vi
    .fn()
    .mockImplementation(() => ({ cancel: vi.fn(), finished: Promise.resolve() }));

// create a delay so the component can render
const waitForRender = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

test('Click on option, multiple false', async () => {
  const subject = document.createElement('atp-select');
  subject.setAttribute('options', JSON.stringify(["v1", "v2", "v3"]));
  document.body.appendChild(subject);
  await waitForRender(0);

  const select = subject.querySelector('.select') as HTMLDivElement;
  await select.click();

  const options = subject.querySelectorAll('.options button') as unknown as HTMLButtonElement[];
  await options[1].click();

  await expect(options[1].outerHTML).include('checkmark');
});

It would be great is this could be added in the documentation somewhere, since this is probally not the most obvious to use when testing components.

@mcous
Copy link
Collaborator

mcous commented Dec 31, 2024

@EricVanEldik great to hear you got it working! I'm going to close this issue since it doesn't involve the @testing-library/svelte library. If you need additional help, the Svelte Discord and/or the DOM Testing Library Discord may be good resources. Otherwise I'm happy to continue helping in this thread.

A couple quick recommendations:

  • Remove anything you add to document.body in a beforeEach or afterEach to make sure your tests don't conflict with each other
  • Use @testing-library/dom and its auto-waiting findBy... queries so you don't have to add a delay yourself
    import { screen } from '@testing-library/dom';
    
    // ...
    const subject = document.createElement('atp-select');
    // ...
    document.body.appendChild(subject);
    const select = await screen.findByRole('select');
  • Avoid mocking Element.prototype.animate, which JSDOM does not provide. Instead:
  • Use @testing-library/user-event rather than calling element.click() for a more accurate simulation of a user interacting with your app
    • Alternatively, use Vitest's browser mode which actually fires up a browser and clicks things

@mcous mcous closed this as completed Dec 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants