Skip to content

Hyphens are stripped from kebab-case attributes when camelCase props present #1190

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
chrisbradleydev opened this issue Mar 19, 2019 · 5 comments

Comments

@chrisbradleydev
Copy link

Version

1.0.0-beta.29

Reproduction link

https://github.com/ctbradley/vue-test-utils-demo

Steps to reproduce

npm i

npm run test

or run using wallaby...

the most relevant files are:

src/components/HelloWorld.vue

src/components/HelloWorld.test.ts

src/components/VForm.vue

comment out props in VForm.vue to demonstrate behavior

What is expected?

VFormImport attributes are expected as kebab-case.

What is actually happening?

VFormImport attributes are being stripped of their hyphens.


Note that this only affects components that are being imported as demonstrated in src/components/HelloWorld.test.ts.

@hectorguo
Copy link

I have the same issue after upgrading Webpack from 1 to 4.

// Before
<loader-bar-stub
   is-loading="true"
/>

// After
<loader-bar-stub
   isloading="true"
/>

I don't know which lib converted it. Maybe babel or vue-test-utils. I would really appreciate if anyone could hep identify the root cause.

I'm using babel 6 and [email protected]

@afontcu
Copy link
Member

afontcu commented Jun 9, 2020

Hi @hectorguo! Just to cross off some stuff from the list, is it possible for you to update to latest version of vue-test-utils (v1.0.3) and check if the issue is still there?

@hectorguo
Copy link

hectorguo commented Jun 10, 2020

Hi @afontcu, thanks for the quick response. I've found the root cause and got a working solution.

The reason is because of this line code:
https://github.com/vuejs/vue-test-utils/blob/dev/packages/create-instance/create-component-stubs.js#L119

Previously, I'm using require(...) to import child components in Vue.
After upgrading to webpack v4, vue-loader deprecated the support for the commonJS. So it's changed to require(...).default.

Before upgrading, the originalComponent is something like { default: VueComponent }. So all options (attrs, props, etc.) are returning undefined.

After changing to require(...).default, the output becomes VueComponent directly, all options can get the correct values.

Root Cause

However, this line code is passing Vue props to HTML attributes.

In Vue render, all HTML attributes will be converted to lowercase since they are case insensitive. So when creating stub components, if the props are camelCase, it will be converted to lowercase instead of kebab-case. It's why the snapshot is different.

export default {
	name: 'LoadingBar',
	props: ['isLoading']
}

// Before
<loading-bar is-loading="true" />


// After
<loading-bar isloading="true" />

Here is a sandbox link to proof that Vue render is lowercasing attrs: https://codesandbox.io/s/vue-render-function-vnul1?file=/src/components/CustomList.vue
image

Solution

There are 2 solutions for it.

  1. change line 119 to
// To make sure there is always a default wrapping the component
// It's working great when upgrading webpack from v1 to v4 
const componentOptions = resolveOptions(originalComponent.default ? originalComponent : { default: originalComponent }, _Vue)
  1. Hyphenate $props
    render: function render(h, context) {
      // it will make sure camelCase props will be converted to kebab-case
      // e.g. `{ isLoading: true } ` to `{ 'is-loading': true }`
      const hyphenatedProps = Object.keys(this.$props).reduce((acc, key) => {
        const hyphenatedKey = hyphenate(key);
        acc[hyphenatedKey] = this.$props[key];
        return acc;
      }, {})
      return h(
        tagName,
        {
          attrs: componentOptions.functional
            ? Object.assign({}, context.props,
                context.data.attrs,
                {class: createClassString(
                  context.data.staticClass,
                  context.data.class
                )})
            : hyphenatedProps
        },
        context ? context.children : this.$options._renderChildren
      )
    }})

I really hope that it can be fixed in current version instead of hacking by myself.

@philgruneich
Copy link

Struggling with this issue since while upgrading to the latest @vue/test-utils version (1.2.1). Stubs' attributes are created as merged lowercase strings, therefore, attributes such as "data-test" become "datatest" and then most selectors are breaking. Is there any temporary fix that can be done?

@ebisbe ebisbe added the bug label Jan 27, 2023
@ebisbe
Copy link
Collaborator

ebisbe commented Feb 15, 2023

#1564 (comment)

@ebisbe ebisbe closed this as completed Feb 15, 2023
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

5 participants