components icon indicating copy to clipboard operation
components copied to clipboard

use components discovery with tests

Open bob-lee opened this issue 4 years ago • 19 comments

Describe the bug Tried this library first time, dynamic imports are working great! About auto scan feature, I found it saves many lines of codes but some tests are getting error:

[Vue warn]: Unknown custom element: <MyComponentB> - did you register the component correctly? ...

this is the case where I have MyComponentA and MyComponentB defined under /components folder and MyComponentA is using MyComponentB.

If I resurrect lines of codes that imports MyComponentB in MyComponentA, the test are working back ok but I wonder if I am missing something?

Additional context [email protected] @nuxt/[email protected] @vue/[email protected]

bob-lee avatar Jun 22 '20 21:06 bob-lee

I think it would be possible to create something that auto imports components at test level. Otherwise @nuxt/components only is usable at pages/layouts level (typically tested via e2e rather than unit) rather than within components.

danielroe avatar Jun 26 '20 08:06 danielroe

Yes, please implement auto imports at test level.

lambda0xff avatar Jul 01 '20 13:07 lambda0xff

As a workaround, I'm registering all components as global Vue components in my jest setup file:

[
  'app/components/MyComponentA.vue',
  'app/components/MyComponentB.vue',
  'ui-kit/components/ComponentA.vue',
  'ui-kit/components/ComponentB.vue',
  'ui-kit/components/ComponentC.vue',
].forEach((path) => {
  const name = path.match(/(\w*)\.vue$/)[1];
  const prefix = path.startsWith('ui-kit/') ? 'Ui' : '';
  Vue.component(`${prefix}${name}`, require(path).default);
});

I could automate this by pulling in the configuration/using globs, but I don't feel the need to make it more complicated than this, especially that I'm working on the project alone.

I've spent the past couple of days messing with my project configuration. Not sure if it's been worth all the trouble, but I hope this helps somebody!

Merott avatar Jul 03 '20 19:07 Merott

As a workaround, I'm registering all components as global Vue components in my jest setup file:

[
  'app/components/MyComponentA.vue',
  'app/components/MyComponentB.vue',
  'ui-kit/components/ComponentA.vue',
  'ui-kit/components/ComponentB.vue',
  'ui-kit/components/ComponentC.vue',
].forEach((path) => {
  const name = path.match(/(\w*)\.vue$/)[1];
  const prefix = path.startsWith('ui-kit/') ? 'Ui' : '';
  Vue.component(`${prefix}${name}`, require(path).default);
});

I could automate this by pulling in the configuration/using globs, but I don't feel the need to make it more complicated than this, especially that I'm working on the project alone.

I've spent the past couple of days messing with my project configuration. Not sure if it's been worth all the trouble, but I hope this helps somebody!

@Merott Can you show your jest config file, please?

harrytran998 avatar Jul 19 '20 02:07 harrytran998

@harrytran998 are you running into a particular issue? I don't have anything special for auto-scanning components in my jest config file:

// jest.config.js

const path = require('path');

module.exports = {
  moduleNameMapper: {
    '^@@?/(.*)$': '<rootDir>/$1',
    '^~~?/(.*)$': '<rootDir>/$1',
    '^vue$': 'vue/dist/vue.common.js',
  },
  moduleFileExtensions: ['ts', 'js', 'vue', 'json'],
  transform: {
    '^.+\\.ts$': 'ts-jest',
    '^.+\\.js$': 'babel-jest',
    '.*\\.(vue)$': 'vue-jest',
  },
  testPathIgnorePatterns: ['<rootDir>/(.vscode|node_modules|cypress)/'],
  setupFilesAfterEnv: [path.resolve(__dirname, 'jest.setup.ts')],
  snapshotSerializers: ['jest-serializer-vue'],
};

Merott avatar Jul 19 '20 12:07 Merott

Thanks @Merott, I ended up doing:

// test/setupTest.js (jest setup)

const components = [
  '../components/MyComponentA.vue',
  '../components/MyComponentB.vue',
]
components.forEach((path) => {
  const name = path.match(/(\w*)\.vue$/)[1];
  Vue.component(`${name}`, require(path).default);
});

then could use auto scan feature freely. Happy for now.

bob-lee avatar Jul 20 '20 03:07 bob-lee

Thanks, I have tried this solution which works. But the duration of my tests went from 20ms to 40ms. Normal, because we load all components before each test suites. I have failed to improve it. Code lines VS unit tests duration, I vote to keep unit test duration low. I hope for a good solution at the end.

JBustin avatar Jul 24 '20 16:07 JBustin

This solution is great, but it fails when some component is contained within auto-scanning components.

ryoju-ohata avatar Oct 16 '20 10:10 ryoju-ohata

The following code can automatically register components.

// jest.setup.js
import path from 'path'
import glob from 'glob'
import Vue from 'vue'

glob.sync(path.join(__dirname, './src/components/**/*.vue')).forEach(file => {
  const name = file.match(/(\w*)\.vue$/)[1]
  Vue.component(name, require(file).default)
})

global.Vue = Vue

ryoju-ohata avatar Oct 16 '20 10:10 ryoju-ohata

I hope someone will give it a clean solution. Just register all the components in the folder is not enough because I use font-awasom-icon component inside the tested component, and I get error:

[Vue warn]: Unknown custom element: <font-awesome-icon>

I can use stubs:

const wrapper = mount(MyComponent, { stubs: ['font-awesome-icon'] })

But it's not clean.

DragonWhisperer avatar Nov 11 '20 11:11 DragonWhisperer

I don't know if it's truly not affecting to any configuration that running on test system..

But i found this configuration working to me, and these setting helps me prevents warning @bob-lee mentions. https://dev.to/bawa_geek/how-to-setup-jest-testing-in-nuxt-js-project-5c84

By adding code in ./test/jest.setup.js

import Vue from 'vue'
import { config } from '@vue/test-utils'

Vue.config.silent = true

// Vue.config.ignoredElements = ['nuxt-link']
// Mock Nuxt components
config.stubs.nuxt = { template: '<div />' }
config.stubs['nuxt-link'] = { template: '<a><slot /></a>' }
config.stubs['no-ssr'] = { template: '<span><slot /></span>' }

and add these lines in config file ./jest.config.js setupFilesAfterEnv: ['./test/jest.setup.js']

fbtwitter avatar Jan 18 '21 13:01 fbtwitter

// test/setupTest.js (jest setup)

const components = [
  '../components/MyComponentA.vue',
  '../components/MyComponentB.vue',
]
components.forEach((path) => {
  const name = path.match(/(\w*)\.vue$/)[1];
  Vue.component(`${name}`, require(path).default);
});

@danielroe Is there something that could be implemented to include auto-scanning? This solution here works, but I can't get @ryoju-ohata 's solution working.

brendan1212 avatar Mar 31 '21 01:03 brendan1212

There are actually two solutions to polyfill components in test environment:

  • Importing .nuxt/components/plugin.js (auto registers to global Vue)
  • Importing .nuxt/components/index.js and register manually

For both, we need a nuxt build step. (an upcoming nuxt dryrun would make it faster to only generate templates)

pi0 avatar Apr 06 '21 20:04 pi0

@pi0 So what is the official way to setup our tests to take advantage of auto import feature? Could there be some documentation added to Nuxt to demonstrate this?

Very difficult to take advantage of the auto import feature with our code if our tests don't work.

nmackey avatar May 12 '21 15:05 nmackey

That's a nice idea @nmackey. I'm currently a little bit overwhelmed by tasks but PR definitely welcome for adding examples or docs. /cc @danielroe @ricardogobbosouza if you are interested to help on this.

pi0 avatar May 12 '21 15:05 pi0

Hello, I wouldn't mind writing the documentation, but I'm unsure what are the steps to actually make it work. :thinking:

Strift avatar Aug 14 '21 11:08 Strift

I got one definitely working solution, based on this comment:

There are actually two solutions to polyfill components in test environment:

Importing .nuxt/components/plugin.js (auto registers to global Vue) Importing .nuxt/components/index.js and register manually For both, we need a nuxt build step. (an upcoming nuxt dryrun would make it faster to only generate templates)

The steps I actually took:

  1. I added setupFiles: ["./.nuxt/components/plugin.js"], to my jest.config.js
  2. I changed my package.json["scripts"]["test"] from just jest to nuxt build && jest

This worked, but it's a significant slowdown for running tests. If I know I have an up-to-date-build, I can delete the nuxt build && (or create a new script without it, e.g. retest, in package.json), but I'm not aware of any automated solution to that.

This specific implementation is to my mind too slow and hacky to enshrine in the official documentation, honestly, even though the programatically-generated .nuxt/components/plugin.js is exactly what the test env needs 😞. I'd love any suggestions on how to improve it.

ambirdsall-gogo avatar Aug 24 '21 00:08 ambirdsall-gogo

Hi! So what would be the official way to solve this issue? BTW, the setup I'm using right now that's completely automated (but not efficient) is to have this in my jest.config.js

module.exports = {
  preset: '@nuxt/test-utils',
  moduleFileExtensions: [
    'js',
    'json',
    'vue'
  ],
  transform: {
    '.*\\.(vue)$': '@vue/vue2-jest',
    '.*\\.(js)$': 'babel-jest'
  },
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
    '^~/(.*)$': '<rootDir>/$1',
    '^vue$': 'vue/dist/vue.common.js',
    '~/([a-zA-Z0-9/.\\-_]*)': '<rootDir>/$1',
    '/^~/(.*)$/': './$1',
    '^.+\\.(css)$': '<rootDir>/test/__mocks__/css.js'
  },
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['./jest.setup.js']
}

And in jest.setup.js

const path = require('path')
const glob = require('glob')
const Vue = require('vue')

glob.sync(path.join(__dirname, './components/**/*.vue')).forEach((file) => {
  const name = file.match(/(\w*)\.vue$/)[1]
  Vue.component(name, require(file).default)
})

global.Vue = Vue

PS: And do you know if this will still be an issue with Nuxt 3 and Vitest?

vencho-mdp avatar Feb 15 '22 19:02 vencho-mdp

I got one definitely working solution, based on this comment:

There are actually two solutions to polyfill components in test environment: Importing .nuxt/components/plugin.js (auto registers to global Vue) Importing .nuxt/components/index.js and register manually For both, we need a nuxt build step. (an upcoming nuxt dryrun would make it faster to only generate templates)

The steps I actually took:

  1. I added setupFiles: ["./.nuxt/components/plugin.js"], to my jest.config.js
  2. I changed my package.json["scripts"]["test"] from just jest to nuxt build && jest

This worked, but it's a significant slowdown for running tests. If I know I have an up-to-date-build, I can delete the nuxt build && (or create a new script without it, e.g. retest, in package.json), but I'm not aware of any automated solution to that.

This specific implementation is to my mind too slow and hacky to enshrine in the official documentation, honestly, even though the programatically-generated .nuxt/components/plugin.js is exactly what the test env needs 😞. I'd love any suggestions on how to improve it.

@ambirdsall-gogo Just having setupFiles: ["./.nuxt/components/plugin.js"] in jest.config.js and run npm run jest is working. Is there any possible issue that I don't see?

arishojaei avatar Apr 04 '23 11:04 arishojaei