Skip to content

Inline Svelte components for tests #14791

Open
@sureshjoshi

Description

@sureshjoshi

Describe the problem

I'll start off by saying I don't know whether this would be a Svelte feature, or maybe part of a Vite plugin, or even if this should be in another library.

When I'm writing component tests using something like testing-library, I'll need to write some code like this (which requires jumping from file to file):

import {render} from '@testing-library/svelte'
import MyComponent from './MyComponent.svelte'

const result = render(MyComponent, componentOptions, renderOptions)

This leaves me jealous of frameworks like React where you can inline JSX and see a lot more of your component code (all of it, if you really wanted to):

import {render, screen} from '@testing-library/react'
import Fetch from './fetch'

test('loads and displays greeting', async () => {
  render(<Fetch url="/greeting" />)

I'm attempting to port Tailwind's HeadlessUI-React library to Svelte 5, and in the process of porting, there are hundreds (thousands?) of inline JSX examples to test components in different ways, eg:

https://github.com/tailwindlabs/headlessui/blob/main/packages/%40headlessui-react/src/components/switch/switch.test.tsx

it('should be possible to render an (on) Switch using a render prop', () => {
  render(
    <Switch checked={true} onChange={console.log}>
      {({ checked }) => <span>{checked ? 'On' : 'Off'}</span>}
    </Switch>
  )
  ...

Writing a custom Svelte file per test would be annoying, but moreso, just trying to debug that becomes a nightmare very quickly (I know, I've tried).

Describe the proposed solution

I have a partial solution that I'm kinda meh about, but think this might be a better first-party plugin or solution. Or maybe I can directly use the compiler or createRawSnippet to facilitate this, but I honestly am not sure about any of that.

Using a hacked together Vite plugin, I - on-the-fly - compile Svelte template strings into virtual modules, and async import them into my test methods.

https://github.com/RobotPajamas/headlessui-svelte/blob/main/src/lib/switch/switch.dom.test.ts

it("should be possible to use in an uncontrolled way", async () => {
    let handleSubmission = vi.fn();

    const component = await sveltify(`
      <script>
        import Switch from "$lib/switch/Switch.svelte";
        let { handleSubmission } = $props();
      </script>
      <form
        onsubmit={(e) => {
          e.preventDefault();
          handleSubmission(Object.fromEntries(new FormData(e.target)));
        }}
      >
        <Switch name="notifications" />
        <button id="submit">submit</button>
      </form>
    `);
    render(component, { handleSubmission });

The plugin is here, just a couple dozen lines of code - cobbled together from other similar plugins I'd seen in the past (and some guesswork) when this library was originally intended for Svelte 3 or 4: https://github.com/RobotPajamas/headlessui-svelte/blob/24ef0380838e50241add1a86dc6aaaad89a8d21b/vite.config.ts#L27-L94

Some things I currently run into (without having spent much time to look into them, as this is "good enough" when compared against my overall goal):

  • Hot-reload isn't working when you change string component content, need to re-run tests from scratch
  • No type safety, until you run tests and see what fails
  • All imports must be explicit, but it would be nice to ambiently import Switch just once in the test file to reduce total lines of code (e.g. 35 lines of import Switch in that one test file)
  • Need to ensure my tests are all asynchronous in order to import the virtual component
  • Requires type hinting via types.d.ts or a do-nothing function in the file to satisfy the type checker (function sveltify(input: string): Promise<typeof SvelteComponent>)

The feature idea would be some way to "natively" (or "pluginly") have a similar result as above. In my ideal world, it wouldn't be a bag of strings without type checking, but something ... else... that someone smarter than me can come up with 😄

Importance

nice to have

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions