testing-library/svelte-testing-library

Problems with testing file upload component

lx4r opened this issue · 4 comments

lx4r commented

Hi,
I'm trying to test a file upload component in a Svelte Kit app. In manual testing the component works fine but I cannot get my test to pass. The handler for the file input fires in the test but the file is null.

I have created a minimal example:

Component file:

<script lang="ts">
  let selectedFiles: FileList | null = null;
  let successMessage = '';
  $: handleFileSelected(selectedFiles);

  async function handleFileSelected(selectedFiles: FileList | null) {
    console.log(`handleFileSelected called with file ${selectedFiles}`);
    const selectedFile = selectedFiles && selectedFiles[0];
    if (!selectedFile) {
      console.log('handleFileSelected: selected file is falsy');
      return null;
    }

    const textInFile = await selectedFile.text();

    if (textInFile === '') {
      console.log('handleFileSelected: selected file has no text');
      return;
    }

    console.log(`File content: ${textInFile}`);

    successMessage = 'File successfully uploaded!';
  }
</script>

<label for="solution-upload-file-input-test"> Upload File </label>
<input type="file" bind:files={selectedFiles} id="solution-upload-file-input-test" />

{successMessage}

Test file:

import { act, fireEvent, render, screen } from '@testing-library/svelte';
import { describe, it } from 'vitest';
import FileUploadTest from './FileUploadTest.svelte';

describe('FileUploadTest', () => {
  it('should upload file', async () => {
    render(FileUploadTest);
    const input = screen.getByLabelText('Upload File');
    const file = new File(['test file content'], 'test.txt', {
      type: 'text/plain',
    });

    await act(() =>
      fireEvent.input(input, {
        target: { files: [file] },
      })
    );

    screen.getByText('File successfully uploaded!');
  });
});

Output of the test:

 DEV  v0.31.1 /home/projects/vitejs-vite-3qzmfw

stdout | src/lib/FileUploadTest.test.ts > FileUploadTest > should upload file
handleFileSelected called with file null
handleFileSelected: selected file is falsy

 ❯ src/lib/FileUploadTest.test.ts (1)
   ❯ FileUploadTest (1)
     × should upload file

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  src/lib/FileUploadTest.test.ts > FileUploadTest > should upload file
TestingLibraryElementError: Unable to find an element with the text: File successfully uploaded!. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

Ignored nodes: comments, script, style
<body>
  <div>
    <label
      for="solution-upload-file-input-test"
    >
      Upload File
    </label>
     
    <input
      id="solution-upload-file-input-test"
      type="file"
    />
     
    
  </div>
</body>
 ❯ getElementError node_modules/@testing-library/dom/dist/config.js:37:19
 ❯ makeGetAllQuery/< node_modules/@testing-library/dom/dist/query-helpers.js:76:38
 ❯ makeSingleQuery/< node_modules/@testing-library/dom/dist/query-helpers.js:52:25
 ❯ wrapSingleQueryWithSuggestion/< node_modules/@testing-library/dom/dist/query-helpers.js:95:24
 ❯ null.<anonymous> src/lib/FileUploadTest.test.ts:19:12
     17|     );
     18| 
     19|     screen.getByText('File successfully uploaded!');
       |                              ^
     20|   });
     21| });

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: {
  "columnNumber": 528297,
  "fileName": "https://vitejsvite3qzmfw-u1dq-boo3i3dp.w-corp.staticblitz.com/blitz.9c8944bc.js",
  "lineNumber": 44,
}
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

 Test Files  1 failed (1)
      Tests  1 failed (1)
   Start at  21:33:31
   Duration  7.42s (transform 505ms, setup 0ms, collect 924ms, tests 46ms, environment 1.78s, prepare 551ms)

It can also be found on Stackblitz here.

How can I get my test to pass?

Thanks in advance for the help :)

You have to do two things:

change fireEvent.input(..) for fireEvent.change(...), and use happy-dom instead of jsdom (well, maybe you already do, I haven't checked). That did it for me. It seems that jsdom doesn't fake all the File object as it should.

I'll assume that did the trick. I won't close the issue yet: I think it deserves an addition to the documentation's FAQs.