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

Inline Svelte components for tests #14791

Open
sureshjoshi opened this issue Dec 20, 2024 · 6 comments
Open

Inline Svelte components for tests #14791

sureshjoshi opened this issue Dec 20, 2024 · 6 comments

Comments

@sureshjoshi
Copy link

sureshjoshi commented Dec 20, 2024

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

@Thiagolino8
Copy link

One solution might be to write the tests in the script module

@paoloricciuti
Copy link
Member

One solution might be to write the tests in the script module

I absolutely love this idea 😍

@sureshjoshi
Copy link
Author

One solution might be to write the tests in the script module

I'm not sure I'm grokking what you're putting down. Let's say my Switch.svelte component has 30 tests in a variety of configurations (switch.test.ts), where would I now put the tests?

@paoloricciuti
Copy link
Member

One solution might be to write the tests in the script module

I'm not sure I'm grokking what you're putting down. Let's say my Switch.svelte component has 30 tests in a variety of configurations (switch.test.ts), where would I now put the tests?

At least from what I understood it should be switch.test.svelte.

You render the component in normally and test it from the module

@sureshjoshi
Copy link
Author

test it from the module

This is the part that's confusing me.

I'd have a single switch.test.svelte and then 30 or so variants of this in the single <script module> Or would each of the tests occupy it's own <script module> in the same test.svelte file?

<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>

@dummdidumm
Copy link
Member

There's https://github.com/DockYard/svelte-inline-component, but no idea if it still works.

So yeah, you could solve this with a Vite plugin, which I feel like is probably the best course of action.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants