Skip to content

Triggering a click on a submit button does not submit the parent form #171

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

Closed
vvanpo opened this issue Aug 5, 2020 · 11 comments
Closed
Labels
bug Something isn't working

Comments

@vvanpo
Copy link

vvanpo commented Aug 5, 2020

I opened this bug in vue-test-utils first: vuejs/vue-test-utils#1635

And then I created a minimal reproduction using [email protected] and @vue/[email protected]: https://github.com/vvanpo/minimal-repros/tree/master/vue-test-utils

The test looks as follows:

import { createApp } from 'vue'
import { mount } from '@vue/test-utils'

function createForm(slot) {
  return {
    template: `
      <form @submit.prevent="submitted = true">
        ${slot}
      </form>
    `,
    data: () => ({ submitted: false }),
  }
}

describe('jsdom', () => {
  test('Clicking a <button> should submit the parent form', () => {
    const vm = createApp(createForm('<button></button>')).mount('body')

    document.querySelector('button').dispatchEvent(
      new window.MouseEvent('click'),
    )

    expect(vm.submitted).toBe(true)
  })

  test('Clicking a <button type="button"> should _not_ submit the parent form', () => {
    const vm = createApp(
      createForm('<button type="button"></button>')
    ).mount('body')

    document.querySelector('button').dispatchEvent(
      new window.MouseEvent('click'),
    )

    expect(vm.submitted).toBe(false)
  })
})

describe('@vue/test-utils', () => {
  test('Clicking a type="submit" should submit the parent form', async () => {
    const wrapper = mount(createForm('<button></button>'))

    await wrapper.get('button').trigger('click')

    expect(wrapper.vm.submitted).toBe(true)
  })
})

Both the pure jsdom examples pass, and give the correct behaviour that browsers also give. But the @vue/test-utils example fails, even though it's using the same component and essentially doing the exact same thing.

@lmiller1990
Copy link
Member

lmiller1990 commented Aug 5, 2020

Thanks you opening this. I would really like to fix this. I have had this same problem for many years and the way I work around is it by doing wrapper.find('form').trigger('submit.prevent').

Interestingly enough if you remove .prevent in your reproduction, you get this error:

console.error
Error: Not implemented: HTMLFormElement.prototype.submit
      at module.exports (/Users/lachlan/code/projects/vue-test-utils-next/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)

Which can probably fixed with a manual event.preventDefault(). Maybe something about .prevent and our event logic is messing things up.

We use the same logic for both VTU 1 and 2 for the trigger stuff. I am sure this is a bug in VTU. I will enlist professional help from the rest of the Vue team on this one 💪

@lmiller1990 lmiller1990 added the bug Something isn't working label Aug 5, 2020
@vvanpo
Copy link
Author

vvanpo commented Aug 5, 2020

That error is logged because the first jsdom test does successfully submit the form, and jsdom doesn't implement any navigation features so it warns you when you submit a form. If I .skip the jsdom block and remove the .prevent, you don't get that error as the @vue/test-utils test still fails and does not submit the form, so I don't think it has to do with .prevent in this case.

I'll try to do some debugging later this week, if I find anything I'll update this issue.

@lmiller1990
Copy link
Member

lmiller1990 commented Aug 5, 2020

I figured the jsdom error was related to not preventing the default submission. I guess how we handle prevent is messing with things?

I also tried this in a browser with VTU and it didn't work there either, so it is definitely not a jsdom error. I would very much like to solve this error - lmk if you have any other ideas or suspicions.

@vvanpo
Copy link
Author

vvanpo commented Aug 5, 2020

I figured the jsdom error was related to not preventing the default submission.

Definitely, the default behaviour of a form submission is not supported by jsdom, hence the error. Calling preventDefault() prevents that behaviour, hence no error.

I guess how we handle prevent is messing with things?

I don't think so, from what I can tell it's working exactly as expected. If we remove .prevent or remove the listener entirely, the error is logged in the jsdom tests, as expected. If we replace the handler with a function that calls preventDefault(), we do not see a logged error, which is the same behaviour as just using .prevent. The problem here is that the test using mount() and trigger() does not trigger the @submit handler, regardless of whether we use .prevent.

I think I can narrow this down further by running the same test but without trigger(), just by using the DOM API directly as I did in the jsdom test. I'll try that now.

@vvanpo
Copy link
Author

vvanpo commented Aug 6, 2020

Okay, I've figured it out, and this isn't a bug after all. The problem was that mount() does not attach the component to the DOM by default, only when you pass the attachTo option. This is consistent with the behaviour you get in the browser.

If I run the following in my browser (Chrome) console:

const el = document.createElement('div')
el.innerHTML = '<form><button></button></form>'
const button = el.querySelector('button')
button.dispatchEvent(new MouseEvent('click'))

I get an error:

VM722:4 Form submission canceled because the form is not connected

If I instead pass { attachTo: document.body } as the mount() options, the test passes. I'm going to close this and the issue I opened in vuejs/vue-test-utils#1635.

@lmiller1990 I'd like to add some clarification to the docs, but it appears they're in another repo? Is that because Vuepress is still using Vue 2?

@vvanpo vvanpo closed this as completed Aug 6, 2020
@lmiller1990
Copy link
Member

Ahhhh that is very interesting. Makes sense!

Yep, the docs are in another repo because Vuepress only works with Vue 2. We will move to Vitepress soon, maybe. If you would like to make a PR in the docs repo that would be great! There is a page for forms specifically: https://vuejs.github.io/vue-test-utils-next-docs/guide/forms.html. The repo is here: https://github.com/vuejs/vue-test-utils-next-docs

@develmac
Copy link

I think I might have a similar issue: I was trying to attach a component that is using Vuetify to the DOM (using happy-dom), but still the click does not submit the form for me.

    const div = document.createElement('div')
    div.id = 'root'
    document.body.appendChild(div)


    const wrapper = mount(MyComp, {
      attachTo: document.body, // or '#root'
      global: {
        plugins: [vuetify],
      },
    })

await wrapper("#btnSubmit").trigger("click")

Using
wrapper('#form').trigger('submit.prevent')
does work!

@cexbrayat
Copy link
Member

@maconic Can you try with jsdom? happy-dom has several issues, I wouldn't be surprised if it's the cause of your issue.
If that doesn't solve it, open a dedicated issue with a minimal reproduction and we'll take a look.

@develmac
Copy link

@cexbrayat Makes no difference sadly (except for that fetch API is not defined in JSDOM, seems to work with happy-dom). I will try to create an example repo with this, if thats OK?

@develmac
Copy link

develmac commented Jul 4, 2022

@cexbrayat turns out it WAS an issue with happy-dom! Works great in the demo project (https://github.com/maconic/vue-vuetify-demo-project) with jsdom. To bad jsdom does not provide mocks for fetch api :(

@cexbrayat
Copy link
Member

Thanks for letting us know 👍 I think you can install whatwg-fetch to have fetch support in jsdom.
But anyway, happy to know this is not a VTU issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants