[SOLVED] [+Vue] Unit Testing

Does someone have a guide on a way to unit test framework7 + vue? There are nice guides for unit testing VUE itself, but because Framework7 shows up in my templates as modules, the tests always barf.

Then, no matter how I try to ā€œimport Framework7ā€ (just like in my main.js file), the tests barf because they canā€™t process the IMPORT statements WITHIN the Framework7 module.

Does ANYONE have unit testing working for Framework7 + Vue? If so, is there any chance you wrote a post or article about it somewhere?

Guides for Vue Unit Testing:
https://vue-test-utils.vuejs.org/guides/choosing-a-test-runner.html
https://vue-test-utils.vuejs.org/guides/testing-single-file-components-with-jest.html
https://vue-test-utils.vuejs.org/

I love working with Vue and really like Framework7. But this lack of Unit Testing for a nice Vue+Framework7 app is a real showstopper. :frowning:

Any ideas?

Iā€™ve found the easiest way is when I do a ā€˜mount(ModuleNameā€™, to include something like stubs: ['f7Page','f7View','f7List','f7ListItem','f7Icon'] in itā€™s options.

Thus the errors are gone, and I donā€™t have to actually import or initialize Framework7. :slight_smile:

I realize this method has itā€™s downsides, as in that I wonā€™t be able to see a generated input (from f7Input), but I think I can work around that.

Iā€™t would be nice though if someone wrote up an article or example of how to properly unit test a Framework7 + Vue module with something simple like one input field.

1 Like

hi @Suthern did you ever see a tutorial or have a demo project showing how to get this all setup. I am doing testing with Vue, Framework7 and Jest and struggling to get it all working.

I havenā€™t written any guides on it, but I did get unit testing to work. Basically when I unit test something I stub out all itā€™s ā€œchildrenā€, then test them separately. Let me see if I can dig up a simple sample.

1 Like

Hereā€™s a small unit test I have for a module called ā€œthumbnailā€. Perhaps youā€™ll find it useful?

import { shallowMount } from '@vue/test-utils';
// import Vuex from 'vuex';
// eslint-disable-next-line import/no-unresolved
import Thumbnail from 'components/thumbnail.vue';

// const localVue = createLocalVue();
// localVue.use(require('vue-moment'));

// localVue.use(Vuex);

describe('Thumbnail', () => {
  let $store = null;
  let mocks = null;
  let propsData = null;

  beforeEach(() => {
    $store = {
      state: { },
      getters: {
        attachmentChangeByID: () => [],
      },
    };
    mocks = {
      $store,
    };
    propsData = {
      attachment: { AttachmentID: 99999, OrigFileName: 'some.cool.jpeg' },
      attachmentTitle: 'Site 349384',
      pageTheme: 'blue',
    };
  });


  it('A jpg shows up in a <img>', () => {
    const wrapper = shallowMount(Thumbnail, {
      propsData,
      stubs: ['f7Button'],
      mocks,
    });

    expect(wrapper.find('img').exists()).toBeTruthy();
  });

  it('A pdf shows up in a ".pdfthumb"', () => {
    const wrapper = shallowMount(Thumbnail, {
      propsData: { ...propsData, attachment: { AttachmentID: 99999, OrigFileName: 'some.cool.pdf' } },
      stubs: ['f7Button'],
      mocks,
    });

    expect(wrapper.find('.pdfthumb').exists()).toBeTruthy();
  });

  it('A MP3 shows up in a google viewer iframe', () => {
    const wrapper = shallowMount(Thumbnail, {
      propsData: { ...propsData, attachment: { AttachmentID: 123456, OrigFileName: 'some.cool.mp3' } },
      stubs: ['f7Button'],
      mocks,
    });

    expect(wrapper.find('iframe').exists()).toBeTruthy();
    expect(wrapper.find('iframe').attributes('src')).toContain('drive.google.com');
  });

  it('Shows "debounced input"', () => {
    const wrapper = shallowMount(Thumbnail, {
      propsData,
      stubs: ['f7Button'],
      mocks,
    });

    expect(wrapper.find('inputdebounced-stub').exists()).toBeTruthy();
  });

  it('Shows button for previewing', () => {
    const wrapper = shallowMount(Thumbnail, {
      propsData,
      stubs: ['f7Button'],
      mocks,
    });

    expect(wrapper.find('.zoom-button').exists()).toBeTruthy();
  });
});

@Suthern Thanks so much for reply. It is more the config side of things iā€™m stuck with. If possible do you have your package.json or babel / jest config. For me it breaks when trying to import any vue component file at all. :frowning:

i have this:

    "jest": {
        "moduleFileExtensions": [
            "js",
            "json",
            "vue"
        ],
        "transform": {
            "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
            ".*\\.(vue)$": "vue-jest"
        },
    },

and these dependencies:

    "devDependencies": {
        "@babel/core": "^7.9.0",
        "@babel/plugin-syntax-dynamic-import": "^7.8.3",
        "@babel/plugin-transform-runtime": "^7.9.0",
        "@babel/preset-env": "^7.9.5",
        "@babel/runtime": "^7.9.2",
        "@vue/test-utils": "^1.0.0-beta.33",
        "babel-core": "^7.0.0-bridge.0",
        "babel-jest": "^25.4.0",
        "babel-loader": "^8.1.0",
        "chalk": "^4.0.0",
        "copy-webpack-plugin": "^5.1.1",
        "cpy-cli": "^3.1.0",
        "cross-env": "^7.0.2",
        "css-loader": "^3.5.2",
        "file-loader": "^6.0.0",
        "html-webpack-plugin": "^4.2.0",
        "jest": "^25.4.0",
        "mini-css-extract-plugin": "^0.9.0",
        "node-sass": "^4.13.1",
        "optimize-css-assets-webpack-plugin": "^5.0.3",
        "ora": "^4.0.3",
        "postcss-loader": "^3.0.0",
        "postcss-preset-env": "^6.7.0",
        "rimraf": "^3.0.2",
        "sass-loader": "^8.0.2",
        "style-loader": "^1.1.3",
        "terser-webpack-plugin": "^2.3.5",
        "url-loader": "^4.1.0",
        "vue-jest": "^3.0.5",
        "vue-loader": "^15.9.1",
        "vue-style-loader": "^4.1.2",
        "vue-template-compiler": "^2.6.11",
        "webpack": "^4.42.1",
        "webpack-cli": "^3.3.11",
        "webpack-dev-server": "^3.10.3"
    }

From what I understand it should be able to process vue files. But i get SyntaxError: Unexpected token export.

I have tried importing a component with no framework 7 components with it at all and still doesnā€™t like it, I realise I can somehow inject the f7 stuff in the transformIgnorePatterns for when using f7 items i think.

if i was running your example test for example my setup would fail here:

import Thumbnail from ā€˜components/thumbnail.vueā€™;

Thanks.

Sure thing! I have a bit more than you might need, but here is my whole package.json file . Havenā€™t updated in in a few months.

{
  "name": "a1-mobile-pwa",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "test:unit:watch": "vue-cli-service test:unit --watch",
    "test:unit": "vue-cli-service test:unit",
    "test:e2e": "vue-cli-service test:e2e --url http://localhost:8080/",
    "test:e2e:attachments": "vue-cli-service test:e2e --url http://localhost:8080/ --test tests\\e2e\\specs\\Attachments.js",
    "test:e2e:bandwidth_test": "nightwatch --url http://localhost:8080/ --test tests\\e2e\\specs\\Bandwidth_Test.js",
    "test:e2e:login": "vue-cli-service test:e2e --url http://localhost:8080/ --test tests\\e2e\\specs\\Login_Page.js",
    "test:e2e:main_gui": "vue-cli-service test:e2e --url http://localhost:8080/ --test tests\\e2e\\specs\\Main_GUI.js",
    "test:e2e:scopes": "vue-cli-service test:e2e --url http://localhost:8080/ --test tests\\e2e\\specs\\Quotes_Scopes.js",
    "test:e2e:site_history": "vue-cli-service test:e2e --url http://localhost:8080/ --test tests\\e2e\\specs\\Site_History.js",
    "test:e2e:switch_wo": "vue-cli-service test:e2e --url http://localhost:8080/ --test tests\\e2e\\specs\\Switch_WO.js",
    "test:e2e:timesheets": "vue-cli-service test:e2e --url http://localhost:8080/ --test tests\\e2e\\specs\\Timesheets.js",
    "test:e2e:work_completed": "vue-cli-service test:e2e --url http://localhost:8080/ --test tests\\e2e\\specs\\Work_Completed.js",
    "test:e2e:workorder_info": "vue-cli-service test:e2e --url http://localhost:8080/ --test tests\\e2e\\specs\\Workorder_Info.js"
  },
  "dependencies": {
    "dotenv": "^8.2.0",
    "exif-js": "^2.3.0",
    "font-awesome": "^4.7.0",
    "framework7": "^5.1.1",
    "framework7-icons": "^3.0.0",
    "framework7-plugin-keypad": "^3.0.1",
    "framework7-vue": "^5.1.1",
    "jest": "^24.9.0",
    "localforage": "^1.7.3",
    "lokijs": "^1.5.8",
    "mime-types": "^2.1.25",
    "pdfjs-dist": "^2.2.228",
    "pinch-zoom-element": "^1.1.1",
    "quagga": "^0.12.1",
    "register-service-worker": "^1.6.2",
    "suncalc": "^1.8.0",
    "vue": "^2.6.6",
    "vue-async-computed": "^3.7.0",
    "vue-jest": "^3.0.5",
    "vue-moment": "^4.1.0",
    "vue-uuid": "^1.1.1",
    "vue2-touch-events": "^1.1.2",
    "vuex": "^3.1.2",
    "vuex-persist": "^2.1.1"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.12.1",
    "@vue/cli-plugin-e2e-nightwatch": "^3.12.1",
    "@vue/cli-plugin-eslint": "^3.12.1",
    "@vue/cli-plugin-pwa": "^3.12.1",
    "@vue/cli-plugin-unit-jest": "^3.12.1",
    "@vue/cli-service": "^3.12.1",
    "@vue/eslint-config-airbnb": "^4.0.1",
    "@vue/test-utils": "^1.0.0-beta.29",
    "axios": "^0.19.0",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "^10.0.3",
    "babel-jest": "^23.6.0",
    "chromedriver": "^79.0.2",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "^5.2.3",
    "jest-fetch-mock": "^2.1.2",
    "mockdate": "^2.0.5",
    "nightwatch": "^1.2.4",
    "selenium-server": "^3.141.59",
    "vue-template-compiler": "^2.5.21"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "@vue/airbnb"
    ],
    "rules": {},
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "overrides": [
      {
        "files": [
          "**/__tests__/*.{j,t}s?(x)"
        ],
        "env": {
          "jest": true
        }
      }
    ]
  },
  "postcss": {
    "plugins": {
      "autoprefixer": {}
    }
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

1 Like

And my bable.config.js

module.exports = {
  presets: [
    '@vue/app',
  ],
  "env": {
    "test": {
      "presets": [
        ["@babel/preset-env", { "targets": { "node": "current" }}]
      ]
    }
  }
};

And my jest.config.js

module.exports = {
  moduleFileExtensions: [
    'js',
    'jsx',
    'json',
    'vue',
  ],
  transform: {
    '^.+\\.vue$': 'vue-jest',
    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
    '^.+\\.jsx?$': 'babel-jest',
  },
  transformIgnorePatterns: [
    '/node_modules/',
  ],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  snapshotSerializers: [
    'jest-serializer-vue',
  ],
  testMatch: [
    '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)',
  ],
  testURL: 'http://localhost/',
  watchPlugins: [
    'jest-watch-typeahead/filename',
    'jest-watch-typeahead/testname',
  ],
  roots: [
    '<rootDir>',
    '<rootDir>/tests',
    '<rootDir>/src',
  ],
  modulePaths: [
    '<rootDir>',
    '<rootDir>/src',
  ],
  setupFiles: [
    './tests/setupJest.js',
  ]
};
1 Like

And hereā€™s my folder layout.
image

the ā€˜thumbnail.spec.jsā€™ is within the tests/unit folder.

1 Like

Thanks for your help. i eventually solved my issue with help from @nolimits4web here: How to set up jest and vue test utils with framework 7 vue project?

:slight_smile:

1 Like