Skip to content
This repository has been archived by the owner on Feb 22, 2024. It is now read-only.

Latest commit

 

History

History
444 lines (313 loc) · 16 KB

DEVELOPER.adoc

File metadata and controls

444 lines (313 loc) · 16 KB

Documentation for Developers

Getting started

Installation of Tools

To get started in the project install the following Tools:

Node and NPM

Download from https://nodejs.org/en/, choose the LTS version. At the time this is written Node 10.x is LTS.

IntelliJ from JetBrains

The "ultimate edition" will allow also JavaScript development.

Vue.js Plugin from Jetbrains for IntelliJ

Ensure that you install the one from JetBrains, not the other Vue.js plugin.
https://plugins.jetbrains.com/plugin/9442-vue-js

AsciiDoc Plugin for IntelliJ

This will allow you to preview and edit the *.adoc-files in the project. They are used for documentation.

Vue.js devtools for Google Chrome

This allows you to inspect your Vue application at runtime as long as you are using the development view. Use F12 as normal to open the Chrome developer tools and you’ll find another tab Vue.
You can inspect the component hierarchy, the state of each component and all events created. As the application grows more complex, you might need to hit the Refresh button to see the up-to-date information.
Vue.js devtools in Chrome Store

Configuration of the tools

In IntelliJ disable the option "Use safe write", otherwise the hot-reload functionality will not work.

Developing the application

Running the application in development mode

A different developer might have updated referenced packages.

npm install

To run using hot-reload development mode use the following command. This will forward all requests to URLs below /rest to https://latest.dukecon.org/javaland/2019/rest. It will also connect to the Dukecon’s Keycloak instance. Therefore you’ll need to be online when developing in this mode. See conf/index.js for details on how to configure this.

npm run dev

To run against a local server backend under http://localhost:8081 the proxy target url needs to be changed in config/index.js:

    proxyTable: {
      '/rest': {
        target: 'http://localhost:8081',
        changeOrigin: true,
        ws: true
      },
      '/img/favicon.ico': {
        target: 'http://localhost:8081',
        changeOrigin: true,
        ws: true
      }
    },

To use a static JSON file with conference data of 2018, just uncomment in Conference.js:

  // base = '/static/'

In this case rest/init.json leads to /static/rest/init.json, where the static file javaland2018.json (under /static/rest/conferences/) is linked to. Please ignore error messages of trying to read keycloak.json, etc.

There is a fallback to the same static JSON file after a timeout when there is no internet connection available. See Conference.js, Images.js and DukeconKeycloak.js for further investigation (inside catch block).

Changing Conference used in development mode

You can edit in the file /config/index.js the section proxyTable to point to another conference in development mode.

No Offline Functionality in development mode

You need to build and run the application in a server instead using development mode to test offline functionality. See Configuring and testing the service workers and PWA functionality.

Updating NPM package versions

To update NPM packages to the next version, please run the following commands:

npm update

To lock the package versions to ensure that they are not incidentially updated by the automatic version resolution of NPM, please check in package-lock.json.

Testing the application

Testing can be performed in the following modes

Testing with NPM

cf. Testing with NPM.

NOTE: Both of the following modes are based on this mode and effectively call npm but are used via Maven which enables a local installation of the desired NPM and Node versions first. It then uses these versions to perform the build and test of the application. On CI (The DukeCon Jenkins) the build and tests are performed via this mode.

Build with Maven (directly)

cf. Testing with Maven (direct)

Build with Maven (in Docker)

cf. Testing with Maven (Docker)

Testing with NPM

Unit testing

To run all unit tests run

npm run unit

You can run all tests in an infinite loop

npm run unitloop
Note
It might happen that PhantomJS terminates due to inactivity. In this case open up the URL in Chrome: http://localhost:9876/
Integration testing (End-to-End testing)

To run the integration tests you need a local Chrome browser installed. You’ll also need a connection to the internet as this will start the frontend in development mode that will proxy all /rest resources to https://latest.dukecon.org/javaland/2019. It will also connect to the Dukecon’s Keycloak instance.

Caution
Chrome does not work for automatic browser testing

This does not work with current versions of Chrome (as they might be installed on your local machine as well as with current Linux distributions as used by Docker). Therefor end-to-end testing is recommended based on the headless mode (see Headless integration testing).

Use the following command to run them:

npm run e2e
Headless integration testing

To run the integration tests in a Firefox browser in the background (headless) use the following command. At the time this is written this still needs a display using xvfb-run. See docker-mvn.sh for details.

npm run e2e_headless

Testing with Maven (direct)

The Maven build is based on the Maven Frontend Plugin. During the build lifecycle a local installation of NPM and Node in the desired versions is performed first:

link:../pom.xml[role=include]

In all subsequent build stages these versions are used

mvn test

can be used to perform the the build and unit test.

mvn verify

can be used to perform the build and test including the end-to-end test with a real browser (see Chrome does not work for automatic browser testing).

mvn verify -Pheadless

Used to run with Firefox instead of Chrome (cf. Headless integration testing)

mvn clean

may be used as usual to clean up the local build.

mvn clean -Pveryclean

also cleans up the NPM and Node installation.

Testing with Maven (Docker)

If you want to run the build as it is done on our CI-Server (Jenkins), please run ./docker-mvn.sh with the deired Maven goals, e.g.,

./docker-mvn.sh clean verify -Pheadless

Offline Functionality

For settings and favourites

All settings are stored in localstorage. This also includes the favourites. This allows small values to be retrieved synchronously and without much overhead.

The browser uses a separate store for each hostname and port. The application adds a prefix of URL path to allow multiple instances of DukeCon to be hosted on the same host. See Settings.js for the implementation.

For the application

This application is a PWA (Progressive Web App).

All static web resources will be pre-loaded by the service worker.

For the data

Caching Strategy fastest: All calls to the /rest URLs will be cached and serviced with the previously downloaded version. If the application is online, a new version will be fetched from the network and will be returned by the next request.

Future versions might differentiate between URLs and choose networkFirst to wait for a given time to get a fresh response.

Configuring and testing the service workers and PWA functionality

See webpack.prod.conf.js section SWPrecacheWebpackPlugin on how to configure it. From a developer’s point of view you should be able to develop the application in offline mode and it will 'just work' in offline mode.

Offline functionality is not available in development mode. It is only available in production mode (that is: when you run npm run build).

To test the offline functionality you will usually deploy it to a server and you’ll need the REST resources to be available in a relative path.

To test it locally there is a switch in Conference.js to run it locally for the JavaLand conference:

Run once to install serve globally

npm install -g serve

Run after every change of the source

npm run build
serve dist

A http server is started and serves the distributed build under http://localhost:5000. If the application is running under port 5000 the backend will be called from https://latest.dukecon.org/javaland/2019/. Currently this hard coded in:

  • Images.js

  • DukeconKeycloak.js

  • Conferences.js

  • SpeakersPage.vue

  • Speaker.vue

TODO: externalize backend url for local testing of offline functionality.

Authentication

This project uses Keycloak for authentication: http://www.keycloak.org/

Keycloak needs to be initialized first, as it will use URL redirects that would other wise interfere with the Vue.js router. The Keycloak client library is wrapped in DuekconKeycloak.js as a singleton for the project.

When the user logs in, an offline token is saved in the local storage of the browser. When the user re-opens the website, the user is automatically signed in using the offline token.

Data Model

The application uses a global data model for events and conference data. It is loaded by Conference.js. To all other components of the application it is readonly. But they will be updated asynchronously when the data is loaded initially, they might be updated with new data periodically as well in the future. Use the references returned to bind them to your model.

Components

A Vue.js app is broken down to components. Each route will be one component. Components for each route are registered in main.js.

Best practices:

  • If the page will interact with the route, i.e. to extract a URL parameter, it can be helpful to separate this part from the real logic of the page. The SpeakerPage.vue (interacts with the route) and Speaker.vue (displays speaker data and can be re-used) is a good example.

  • A sub-components can emit events to notify the parent. For example FilterEvents.vue emits an event to give the latest status if the filters are open or closed. The parent component sets a corresponding CSS class that shows/hides the filters in mobile mode.

  • If some changes in a component need to be broadcasted to (potentially) multiple other components or if the components are not in the same hierarchy, use the application’s eventbus. The eventbus is initialized in the Eventbus.js as a mixin. The instance is available within every other Vue instance.
    It has been first used within the FilterEvent.vue component:

    publishFilterSettings: function () {
      /* ... */
      this.eventbus.$emit('filter.status', filter)
    },

    It is consumed for example within the Schedule.vue:

    created () {
      this.eventbus.$on('filter.status', this.filterEventReceived)
    },
    beforeDestroy: function () {
      this.eventbus.$off('filter.status', this.filterEventReceived)
    },
    methods: {
      filterEventReceived (filter) {
        this.filter = filter
      }
    }
  • Events should have a prefix that identifies the component (for example "filter.")

Note
you can inspect all events using the Vue developers tools by looking at the Events tab.

Internationalization

This application used vue-i18n.

Add all messages keys to Internationalization.js. To present a translated key in your component, add code like this in JavaScript or the HTML templates.

Code to be used in JavaScript
$t('imprint')
Code to be used as part of HTML Template
<a>{{ $t('imprint') }}</a>

Integration of Vis in the timetable view

How it works

For the timetable view in TimetablePage.vue the visualisation library Vis Timetable is used. This has no native Vue.js integration, therefore we take the following approach:

  1. once the Timetable component is bound to the DOM initialize Vis Timetable with dummy elements for each event (method draw()). Each dummy event has the event ID as unique DOM ID.

  2. once the Vis Timetable is rendered, call rebindVueTimetableItems() binds the event components to each dummy event using the event’s DOM IDs. This then renders the content of the events and replaces the dummy content.

  3. When the Timetable is reset, moved, zoomed other events are bound as they become visible.

Integration in Vue, Webpack and Babel

There was an issue that the distribution version of Vis already packages a Moment.js version.

Therefore the needed Vis modules are imported directly, and transpiled using Babel with the Babel settings of Vis. See the import in TimetablePage.vue and the babel-loader settings in webpack.base.conf.js for details. A side effect is that the necessary Babel dependency for Vis (babel-preset-es2015) is included also in this project’s development dependencies in the package.json file.

Localization

To show dates and times for different locales, we use MomentJS.

We don’t use a global setting for the locale of MomentJS to support calculated functions for localized dates. Please use the following sample code to format a given date:

// pick any given date
Moment(this.event.start)
  // set the locale for this instance
  .locale(this.$i18n.locale)
  // format it as needed
  .format('dddd, Do MMM, HH:mm')

At the moment only the en- (default) and de-locales are imported, seel ContextReplacementPlugin in webpack.base.conf.js.

  • a search box exists in header

  • you can search for events on Talks page with title, abstract, speaker name and company

  • you can search for speakers on Speaker page with speaker name and company

  • after each keypress a event will fired and catched of TalksGrid and SpeakersPage

  • the search input will be reset on filter reset

Each component that wants the search box should use the mixin SearchMixin: This will toggle the visibility of the search box and subscribe to changes in the search term.

Miscellaneous

  • All router views are cached using the <keep-alive/> tag. This avoid long initial rendering times when returning on the SpeakersPage. It should be re-evaluated if this needs too much RAM.

  • If you have multiple images on a page, consider lazy loading of the images (see SpeakersPage.vue uses VueLazyLoad for this.

  • The scrolling position for each page is saved and restored on route navigation (see initialization of VueRouter). This is not using the HTML5 router as this requires the server file handling to be updated as all routes of the frontend will also appear as bookmarks in the backend.

Optimization for production

The command npm run build creates the folder dist. This is served as static pages from a production server.

You can analyze the contents of the created files by running npm run build --report. This creates and serves a report.html file in the dist folder. Use it to analyze the contents and sizes of the bundle created. The smaller the size, the faster the app will load.

The following files are not needed in production and should be excluded:

report.html

Bundle analyzer report (created by npm run build --report)

*.map

Source Map Files

Testing

Testing components for unit tests

Some best practices - see the unit tests in test/unit/specs for examples.

  1. Always test one component at a time.

  2. To mock calls to other components, use the sinon Sandbox

  3. To mock XMLHttpRequest calls by axios, use moxios

  4. To test a Vue component you’ll need to crate a Vue instance and mount it. This can contain a minimal template and also other components.

  5. Once something changes in the view, wrap the next part in vm.$nextTick