rob-balfre / svelte-select

Svelte Select. A select component for Svelte
https://svelte-select-examples.vercel.app
Other
1.25k stars 175 forks source link

simple example with @testing-library/svelte #646

Open pricetula opened 7 months ago

pricetula commented 7 months ago

Fantastic package however, I seem to have hit a snug trying to set focus and find available options.

// select-component.svelte

<Select
  options={[
    { value: 'val-1', label: 'label 1'},
    { value: 'val-2', label: 'label 2'}
  ]}
  placeholder="placeholder text"
/>

//select-component.test.ts

const selectInput = getByPlaceholderText('placeholder text');
expect(selectInput).toBeTruthy(); // passed
await fireEvent.focus(selectInput);
expect(getByText('label 1')).toBeTruthy(); // failed

Would be great to have a small example on how to test

axel7083 commented 1 week ago

Okey after some work on it, I was able to make it works, cc @rob-balfre would be interesting to have a better way of doing this, not sure how as I've never design component to be properly testable with the svelte testing-library but this is very not ideal.

SelectDemo.svelte

<script lang="ts">
import Select from 'svelte-select';

let collection = [
  { value: 'one', label: 'One' },
  { value: 'two', label: 'Two' },
  { value: 'three', label: 'Three' },
];

let value: { value: string, label: string } | undefined;
</script>

<Select placeholder="Please select" items={collection} bind:value />

<span role="status">{value?.label || ''}</span>

SelectDemo.spec.ts

import { expect, test, vi } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/svelte';
import SelectDemo from '/@/SelectDemo.svelte';

// very important to mock
window.HTMLElement.prototype.scrollIntoView = function() {};

async function selectOption(container: HTMLElement, label: string): Promise<void> {
  // first get the select input
  const input = screen.getByPlaceholderText('Please select');
  await fireEvent.pointerUp(input); // they are using the pointer up event instead of click.

  // get all options available
  const items = container.querySelectorAll('div[class~="list-item"]');
  // ensure we have two options
  expect(items.length).toBeGreaterThan(0);

  // get the option we are interested in
  const remoteModelOption = Array.from(items).find(item => item.textContent?.trim() === label);
  if(!remoteModelOption) throw new Error('missing options in select');

  // click on it
  await fireEvent.click(remoteModelOption);
 // They are storing the current item in an input with hidden type.
  return await vi.waitFor(() => {
    const input = container.querySelector('input[name="select-model"][type="hidden"]');
    if(!input) throw new Error('input not found');
    expect(JSON.parse((input as HTMLInputElement).value).label).toBe(label);
  });
}

test('Select option Two', async () => {
  const { container } = render(SelectDemo);

  await selectOption(container, 'Two');

  // Ensure the selected value is the one we choose
  await vi.waitFor(() => {
    const span = screen.getByRole('status');
    expect(span).toBeDefined();
    expect(span.textContent).toBe('Two');
  });
});