- Getting started
- Installation of Tools
- Configuration of the tools
- Developing the application
- Testing the application
- Testing with NPM
- Testing with Maven (direct)
- Testing with Maven (Docker)
- Offline Functionality
- Authentication
- Data Model
- Components
- Internationalization
- Integration of Vis in the timetable view
- Localization
- Search
- Miscellaneous
- Optimization for production
- Testing
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
In IntelliJ disable the option "Use safe write", otherwise the hot-reload functionality will not work.
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).
You can edit in the file /config/index.js the section proxyTable to point to another conference 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.
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)
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/ |
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
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
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.
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.
This application is a PWA (Progressive Web App).
All static web resources will be pre-loaded by the service worker.
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.
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.
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.
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.
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. |
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.
$t('imprint')
<a>{{ $t('imprint') }}</a>
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:
-
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.
-
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.
-
When the Timetable is reset, moved, zoomed other events are bound as they become visible.
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.
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.
-
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.
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
Some best practices - see the unit tests in test/unit/specs for examples.
-
Always test one component at a time.
-
To mock calls to other components, use the sinon Sandbox
-
To mock XMLHttpRequest calls by axios, use moxios
-
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.
-
Once something changes in the view, wrap the next part in vm.$nextTick