5
votes

I want to use data-test attributes (as suggested here), so when running tests I can reference tags using these attributes.

  <template>
    <div class="card-deck" data-test="container">content</div>
  </template>

The element will be found using:

document.querySelector('[data-test="container"]')
// and not using: document.querySelector('.card-deck')

But I don't want these data-test attributes to get to production, I need to remove them. How can I do that? (There are babel plugins that do that for react.)

Thanks!

2

2 Answers

4
votes

The solution (for a Nuxt.js project), provided by Linus Borg in this thread, is:

// nuxt.config.js
module.exports = {
  // ...
  build:   {
    // ...
    extend(config, ctx) {
      // ...
      const vueLoader = config.module.rules.find(rule => rule.loader === 'vue-loader')
      vueLoader.options.compilerModules = [{
        preTransformNode(astEl) {
          if (!ctx.dev) {
            const {attrsMap, attrsList} = astEl
            tagAttributesForTesting.forEach((attribute) => {
              if (attrsMap[attribute]) {
                delete attrsMap[attribute]
                const index = attrsList.findIndex(x => x.name === attribute)
                attrsList.splice(index, 1)
              }
            })
          }
          return astEl
        },
      }]
    }
  }
}

where tagAttributesForTesting is an array with all attributes to be removed, like: ["data-test", ":data-test", "v-bind:data-test"].

0
votes

For those of you who want to know how to do this is vanilla Vue 3, read on.

According to the Vue CLI documentation, the correct way to override a loader's options is to use the chainWebpack method within your Vue configuration (within your vue.config.js file):

module.exports = {
  chainWebpack(config) {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap((options) => {
        options.compilerOptions.modules = [{
          preTransformNode(element) {
            if (process.env.NODE_ENV !== 'production') return

            const { attrsMap, attrsList } = element;

            if ('data-test' in attrsMap) {
              delete attrsMap[attribute];

              const index = attrsList.findIndex(x => x.name === attribute);
              attrsList.splice(index, 1)
            }

            return element;
          }
        }];

        return options;
      });
  }
};

For your particular use case, I think the most maintenance free option would be to use a pattern matching strategy to remove test attributes. This would keep you from having to add every new test attribute to the list of blacklisted attributes:

{
  preTransformNode(element) {
    if (process.env.NODE_ENV !== 'production') return

    const { attrsMap, attrsList } = element;

    for (const attribute in attrsMap) {
      // For example, you could add a unique prefix to all of your test
      // attributes (e.g. "data-test-***") and then check for that prefix
      // using a Regular Expression
      if (/^data-test/.test(attribute)) {
        delete attrsMap[attribute];

        const index = attrsList.findIndex(x => x.name === attribute);
        attrsList.splice(index, 1)
      }
    }

    return element;
  }
}

Note that the attributes will include any Vue directives (e.g. "v-bind:") that you attach to them, so be sure to compensate for that if you decide to identify your test attributes using unique prefixes.

I think it would be best to mention that, just like @ahwo before me, I drew my inspiration from Linus Borg's suggestion on the Vue forums.

P.s. With Vue, it's possible to create attributes that have dynamic names. I think this would be useful to know for anyone who is adding attributes for testing