[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