Ardor JS & Vue

JS style

Airbnb style guide: https://github.com/airbnb/javascript

Vue style

Ardor Architecture

In the pages folder you can find the top level components, the ones that are directly called by router-links. The rest of the components are found under the components folder and namespaced according to the object they refer to. Global functions and bits of code are found under the mixins folder, even though they are not always proper Vue mixins. The I18n is in the locale folder.

The store is also namespaced according to objects. Each object has a constant.js file containing the constants, an index.js files containing all actions, getters, and mutation, and a service.js file containing the api methods and routes for the object. Some of them also have an initialState.js when necessary.

Components order

We order component options following the guidelines of the Vue style guide: https://vuejs.org/v2/style-guide/#Component-instance-options-order-recommended

Attributes order

We order element attributes following the guidelines of the Vue style guide: https://vuejs.org/v2/style-guide/#Element-attribute-order-recommended. We place all HTML tags under part 8. Other attributes. Inside these we put the props first. We differentiate bound attributes (with v-on or the ':' shorthand) which come first from the ones that are not bound. So the general order should look like this:

  • All Vue attributes from points 1 to 7 from the Vue style guide

  • Props

  • Unspecified bound attributes

  • Unspecified unbound attributes

  • Vue event attributes (v-on, shorthand @)

  • Vue content attributes (v-text, v-html)

el-button(
v-if="showButton"
id="offerCardSubmissionButton"
:loading="offersLoading"
:class="flavor"
:disabled="buttonDisabled"
role="button"
@click="loadOffers"
)

Imports order

We order imports as follow and by alphabetical order:

  • Components imports

  • Mixins imports

  • Vuex import (mapActions, mapGetters, mapState, etc...)

  • Other imports

import DocumentUpload from '@/components/inputs/DocumentUpload'
import InsurerSelect from '@/components/inputs/InsurerSelect'
import { handlers } from '@/mixins/handlers'
import { required } from '@/mixins/validationRules'
import { mapActions, mapState, } from 'vuex'

Naming

We name boolean attributes that will determine whether or not a modal is visible like this:

  • <function>ModalVisible in components with one modal where the object can be inferred from context.

  • <function><Object>ModalVisible when there is more than one modal in the component or if the object is different from the one the component refers to

  • visiblewhen the component itself is nothing but one modal

We rename the imported attributes and functions in mapState and mapActions if they are not directly related to the object the component refers to like this:

  • <object><Attribute> for an attribute

  • <function><Object> for a function

Forms

We try to reuse components as much as possible and therefore extract a form field into a separate component whenever it is used more than once. We use the element ui native validator rules to validate our forms.

Linting

We use the vue plugin of eslint set at the 'recommended' level to lint our code. To lint automatically just run npm run lint. Install the atom package and activate lint on save in the setting to automatically lint the file at every save.

Testing

We test our Vue apps according to the front-end testing pyramid, except for the snapshot testing (checking that UI doesn't change unexpectedly) which we don't do here. For unit testing, we need to test the store (getters, mutations and actions) as well as the components. We only test the store parts and components that contain some kind of logic.

Unit testing

For the store testing, we check if the state is being correctly updated (for mutations), if the returned value is correct (for getters), or if a mutation or another action is being called with the right arguments (for actions).

For the components testing, it is best practice not to focus testing on line-based coverage but rather on the public interface. This means we provide the component with some input (user action, props, store) and check the output (rendered output, events, function calls), treating the internal logic as a black box. The benefit of working this way is that tests don't have to be updated every time we change a component, as long as the output stays the same. To provide input and check the output, we fake a store, giving the component only the state attributes, getters and actions that it needs.

For unit testing, we use Vue-test-utils and Jest, as Jest is a very complete library that includes a test runner, an assertion library, mocking, snapshot testing etc. To run the unit tests, run jest test/unit/*, Jest will pick up all the files that end with .spec.js.

End to end testing

For end-to-end testing we use Cypress, which comes with an integrated assertion library and also gives us the possibility to watch our tests live in the browser, which is very useful for debugging and writing tests. To run the tests in the browser, run node_modules/.bin/cypress open. To run the tests in the terminal, type cypress run --spec testfile.

The objective is to test the main integrations between components and store, as we are already testing the correct functioning of both of these apart. To do this, we start by visiting the the desired url and then do the required actions, which is very easy using Cypress's API commands. We then check if the state has been updated accordingly.

To control the data we are using, we want to stub the API calls we make.

To fake an API back-end, it is worth having a look at json-server which is easy to implement and very powerful.

When we're working with element-ui, we can't use the built-in select()andsetSelected()that Cypress and Vue-test-utils provide because the el-select element doesn't include an HTML select, which means we have to select an option by index.

It is currently not possible to stub an API call dynamically in function of the body you're sending the API endpoint. Cypress is working on implementing this among other things: https://github.com/cypress-io/cypress/issues/687.

Javascript testing 2018

Unit testing store

Components testing

E2E testing

General