Acceptance Testing of a Feature in Open Event Frontend

In Open Event Frontend, we have integration tests for ember components which are used throughout the project. But even after those tests, user interaction could pose some errors. We perform acceptance tests to alleviate such scenarios.

Acceptance tests interact with application as the user does and ensures proper functionality of a feature or to determine whether or not the software system has met the requirement specifications. They are quite helpful for ensuring that our core features work properly.

Let us write an acceptance test for register feature in Open Event Frontend.

import { test } from 'qunit';
import moduleForAcceptance from 'open-event-frontend/tests/helpers/module-for-acceptance';

moduleForAcceptance('Acceptance | register');

In the first line we import test from ‘ember-qunit’ (default unit testing helper suite for Ember) which contains all the required test functions. For example, here we are using test function to check the rendering of our component. We can use test function multiple times to check multiple components.

Next, we import moduleForAcceptance from ‘open-event-frontend/tests/helpers/module-for-acceptance’ which deals with application setup and teardown.

test('visiting /register', function(assert) {
  visit('/register');

  andThen(function() {
    assert.equal(currentURL(), '/register');
  });
});

Inside our test function, we simulate visiting  /register route and then check for the current route to be /register.

test('visiting /register and registering with existing user', function(assert) {
  visit('/register');
  andThen(function() {
    assert.equal(currentURL(), '/register');
    fillIn('input[name=email]', [email protected]');
    fillIn('input[name=password]', 'opev_test_user');
    fillIn('input[name=password_repeat]', 'opev_test_user');
    click('button[type=submit]');
    andThen(function() {
      assert.equal(currentURL(), '/register');
      // const errorMessageDiv = findWithAssert('.ui.negative.message');
      // assert.equal(errorMessageDiv[0].textContent.trim(), 'An unexpected error occurred.');
    });
  });
});

Then we simulate visiting /register route and register a dummy user. For this, we first go to /register route and then check for the current route to be register route. We fill the register form with appropriate data and hit submit.

test('visiting /register after login', function(assert) {
  login(assert);
  andThen(function() {
    visit('/register');
    andThen(function() {
      assert.equal(currentURL(), '/');
    });
  });
});

The third test is to simulate visiting /register route with user logged in and this is very simple. We just visit /register route and then check if we are are at / route or not because a user redirects to / route when he tries to visit /register after login.

And since we checked for all the possible combinations, to run the test we simply use the following command-

ember test --server

But there is a little demerit to acceptance tests. They boot up the whole EmberJS application and start us at the application.index route. We then have to navigate to the page that contains the feature being tested. Writing acceptance tests for each and every feature would be a big waste of time and CPU cycles. For this reason, only core features are tested for acceptance.

Resources

Maintain Aspect Ratio Mixin on Open Event Frontend

The welcome page of the Open-Event-Frontend is designed to contain cards that represent an event. A user is directed to the event-details page by clicking on the corresponding card. The page consists of an image that serves as the banner for the event and an overlapping div to provide some contrast against the image. A comment may also be added onto the image and along with the overlapping div it is wrapped in a container div.

Since we have given a specific height to the contrasting div, the background image shrinks according to the screen size but the contrasting div does not whenever we go from a large screen to a smaller screen.

Mobile view (before):-

We want our contrasting div also to resize in accordance to the image. To do it, we first define a sass mixin to maintain a common aspect ratio for image and overlapping div. Let us see it’s code.

 @mixin aspect-ratio($width, $height) {
    position: relative;
    &:before {
      display: block;
      content: "";
      width: 100%;
      padding-top: ($height / $width) * 100%;
    }
    > .content {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
    }
    > img {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
    }
  }

So what does this mixin actually doing is we are passing the height and width of the image and we are defining a pseudo element for our image and give it a margin top of (height/width)*100 since this value is related to image width (height: 0; padding-bottom: 100%; would also work, but then we have to adjust the padding-bottom value every time we change the width). Now we just position the content element and image as absolute with all four orientations set to 0. This just covers the parent element completely, no matter which size it has.

Now we can simply use our mixin by adding the following line of code in out container div

@include aspect-ratio(2, 1);

Here we want to maintain a 2:1 aspect ratio and the user is also expected to upload the image in the same aspect ratio. Therefore, we pass width as 2 and height as 1 to our mixin.

Now when we resize our screen both the image and the overlapping div resize maintaining 2:1 aspect ratio.

Mobile view (after):-

Resources

  • mademyday blog describes a method of using pseudo elements to maintain an element’s aspect ratio.
  • css-tricks snippet.

Image Cropper On Ember JS Open Event Frontend

In Open Event Front-end, we have a profile page for every user who is signed in where they can edit their personal details and profile picture. To provide better control over profile editing to the user, we need an image cropper which allows the user to crop the image before uploading it as their profile picture. For this purpose, we are using a plugin called Croppie. Let us see how we configure Croppie in the Ember JS front-end to serve our purpose.

All the configuration related to Croppie lies in a model called cropp-model.js.

 onVisible() {
    this.$('.content').css('height', '300px');
    this.$('img').croppie({
      customClass : 'croppie',
      viewport    : {
        width  : 400,
        height : 200,
        type   : 'square'
      },
      boundary: {
        width  : 600,
        height : 300
      }
    });
  },

  onHide() {
    this.$('img').croppie('destroy');
    const $img = this.$('img');
    if ($img.parent().is('div.croppie')) {
      $img.unwrap();
    }
  },

  actions: {
    resetImage() {
      this.onHide();
      this.onVisible();
    },
    cropImage() {
      this.$('img').croppie('result', 'base64', 'original', 'jpeg').then(result => {
        if (this.get('onImageCrop')) {
          this.onImageCrop(result);
        }
      });
    }

There are two functions: onVisible() and onHide(), which are called every time when we hit reset button in our image cropper model.

  • When a user pushes reset button, the onHide() function fires first which basically destroys a croppie instance and removes it from the DOM.
  • onVisible(), which fires next, sets the height of the content div. This content div contains our viewport and zoom control. We also add a customClass of croppie to the container in case we are required to add some custom styling. Next, we set the dimensions and the type of viewport which should be equal to the dimensions of the cropped image. We define type of cropper as ‘square’ (available choices are ‘square’ and ‘circle’). We set the dimensions of our boundary. The interesting thing to notice here is that we are setting only the height of the boundary because if we pass only the height of the boundary, the width will be will be calculated using the viewport aspect ratio. So it will fit in all the screen sizes without overflowing.

The above two functions are invoked when we hit the reset button. When the user is satisfied with the image and hits ‘looks good’ button, cropImage() function is called where we are get the resulting image by passing some custom options provided by croppie like base64 bit encoding and size of cropped image which we are set to ‘original’ here and the extension of image which is we set here as ‘.jpeg’. This function returns the image of desired format which we use to set profile image.

Resources

Integration Testing of an Ember Component in Open Event Frontend

Open Event Frontend uses ember components which are reused several times throughout the project, making these components is one thing but we should also ensure they work as expected. To ensure this we need integration tests.How to write an integration test for event-map component? Three files are generated when we generate our event-map component from the command shell. They are namely:

event-map.js

event-map.hbs

event-map-test.js

We are familiar with the above three files as:

  1. event-map.js helps us to provide properties to our event-map.hbs
  2. event-map.hbs is where we define the structure of our component.
  3. event-map-test.js is where we define integration test for our component. Also, which is the current topic for discussion.

Testing

In order to test the rendering of the component, we define an integration test for that component. In integration tests we don’t have to launch our whole application and navigate to the location where our component is present. This makes it ideal for testing components. Have a look at the following sample integration test file.

import { test } from 'ember-qunit';
import moduleForComponent from 'open-event-frontend/tests/helpers/component-helper';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('public/event-map', 'Integration | Component | public/event map');

let event = Object.create({ latitude: 37.7833, longitude: -122.4167, locationName: 'Sample event location address' });

test('it renders', function(assert) {
  this.set('event', event);
  this.render(hbs `{{public/event-map event=event}}`);
  assert.equal(this.$('.address p').text(), 'Sample event location address');
});

So let’s break down this code line by line-

In the first line we have imported test from ‘ember-qunit’ (default unit testing helper suite     for Ember) which contains all the required test functions. For example, here we are using test function to check to check the rendering of our component. We can use test function multiple times to check multiple components.

Next, we are importing moduleForComponent from ‘open-event-frontend/tests/helpers/component-helper’ helper which helps in finding the component by its name.

Next,  we are importing hbs from ‘htmlbars-inline-precompile’ which basically imports precompile HTMLBars template strings within the tests via ES6 tagged template strings.

The moduleForComponent helper will find the component by name (event-map) and its template. The component we are testing here is a map-related. Therefore, to test this, we need to pass a dummy object consisting of latitude, longitude and locationName. The object data must render correctly in our app.

Inside our test function, this.set(‘event’, event) assigns a variable ( here event ) to our test context.

this.render (hbs `{{public/event-map event=event}}`) lets us create a new instance of the component by declaring the component in template syntax, as we would in our application.

assert.equal(this.$(‘.address p’).text(), ‘Sample event location address’) is basically a check between actual and expected arguments.

For a simple component like a table or a basic UI only component, it is not necessary to pass any object to the component. Only the rendered component can be tested as well. Apart from checking the component rendering we can perform tests on it by using test functions as described above. After writing the integration tests for components, simply run ember test –server on the terminal to see if all the tests have passed or not.      

Find out more about Integration testing in ember –

Ember guides, EmberIgniter

Using Dynamic segments to Reduce Code Redundancy of Recurring HTML in Open Event ember Frontend

While developing web apps, at times we require the same HTML for different pages in our app. This leads to redundancy and low code-reusability. This can be well managed in ember.js by using dynamic segments in our routes.

In Open Event Front-end we have a route named /sessions where we want to show the details of the event’s sessions and we want to categorize the sessions in all, pending, accepted, confirmed and rejected sessions hence want to create the following subroutes under it.

events/<event-id>/sessions
events/<event-id>/sessions/pending
events/<event-id>/sessions/accepted
events/<event-id>/sessions/confirmed
events/<event-id>/sessions/rejected

All of these subroutes show different data based on the routes in a table with exactly same fields. So if we use dynamic segments, we can decrease code redundancy and increase code reusability. So let us see how to add these subroutes as dynamic segments.

Firstly, we have to add a dynamic part (pending, accepted, confirmed, rejected) under our URL /sessions . For this, edit the following code snippet to the router.js file. In place of list write the name of route handler which handles our subroutes and in place of session_status write any identifier you want as session_status is the dynamic part which changes according to subroutes. In our case it will be pending, accepted, confirmed or rejected.

     this.route('sessions', function() {
        this.route('list', { path: '/:session_status' });
      });

To display all the sessions in our /sessions route, we have to edit index route handler and return data from model hook. Now when we hit  /sessions end point, the template which we are using redundantly, i.e. list.hbs, gets data from a model hook of this route handler.

Next we need to define a model hook in our list.js file which returns data for the dynamic routes. In list.js, we would want to change the title of our page according to the dynamic segments. These dynamic segments are available under model hook in this route under param parameter. We are using this.set which sets the provided key or path to the value. Make this available in titleToken function and by applying simple switch case, we can change the title dynamically.

Till now, we could access our dynamic segments by manually changing the URL. Let’s add a link to do this transition automatically for us. For this, we edit our session.hbs file and provide links using a linkto helper. One thing to take care here is that we should pass the dynamic segments along with the link-to helper.

{{#link-to 'events.view.sessions.list' 'pending' class='item'}}

Here, pending is the dynamic segment which we are passing to our route handler. Similarly, we can make the links for all our dynamic segments. Also in this template, we should provide outlets for the common template i.e list.hbs which will be reused by other dynamic subroutes. And finally, we define our reusable template in list.hbs file.

Now when we hit on different links we are redirected to different routes which are using different data but same templates. Also, we can see this transition in our URL and title.

To know more about dynamic segments refer to Ember guide.

Implementing Google Maps in Open Event ember Front-end

In the deployment of the Open Event Front-end on eventyay.com we want to show an event’s location on a map using Google Maps. Since Eventyay.com requires Google map to be used at several web pages, we can use Google map as an ember component for reusability.

How did we do it? The following walks you through the entire process.

The first step is to: Install g-map addon-

  • ember g-map, which is an addon, is available on ember add-on library which can be found at emberobserver.com. So first import that library using ember cli command
ember install ember-g-map
  • This command adds an ember-g-map module in our node_modules and also updated package.json file of our app now our ember-g-map modules contains the following directories. The following three are required to be highlighted that serves our purpose-
  • Addon/ – this directory contains all the implementation logic which acts as a prototype for the g-map component
  • App/- this directory basically exports a copy of g-map from our Addon/ directory to the web page where it is required. The export includes the logic implement inside the Addon/ directory which can later be imported by the parent app for use.Package.json – this file holds various metadata relevant to the ember-g-map

Generate Component-

  • The next step is to generate a component event-map (we can name our component anything we want but be sure to add a hyphen between the name). The component can be generated with the help of the following command of ember cli-
ember g component public/event-map

This will add three files in our app –

event-map.js located at app/component/public/event-map.js

event-map.hbs located at app/template/component/public/event-map.hbs

event-map-test.js located at tests/integration/component/public/event-map-test.js

Define Component-

Now let us use these files to add a google map component in our app-

  • First of all, we have to tell index.hbs about our new component that we are going to add. So we add our component {{public/event-map}} in our index.hbs file which is located at app/templates/public/index.hbs And we fetch latitude, longitude and location name from model and store in the event object and pass it to our component so that this information is available in the component.
<div class="location">
<h1 id="getting-here">{{t 'Getting Here'}}</h1>
 {{public/event-map event=model.event}}
</div>
  • The event-map.js file will be used to add ui grid class to assign a class to the div of the component. This will alleviate the redundancy regarding the creation of an extra div to enclose the component and then giving it the ui grid class.
import Ember from 'ember';
 
const { Component } = Ember;
 
export default Component.extend({
 classNames: ['ui', 'stackable', 'grid']
});
  • In our event-map.hbs we have with us the latitude, longitude, and locationName available and we are good to use our g-map addon. So in our event-map.hbs we can directly add g-map and g-map-marker but make sure that we pass lat and lang attributes to it. We can use custom options like backgroundColor, draggable etc. but here we have used street view and gestureHandling. gestureHandling is set as “cooperative” so that for smaller screen device,  zooming is possible only with two fingers so that scrolling would become easy.
<div class="eight wide column event-map">
 {{#g-map lat=event.latitude lng=event.longitude 
 zoom=12 gestureHandling='cooperative' 
 streetView='StreetViewPanorama' as |context|}}
 {{g-map-marker context lat=event.latitude lng=event.longitude}}
 {{/g-map}}
</div>
<div class="eight wide column address">
 <h1>{{t 'Address'}}</h1>
 <p>{{event.locationName}}</p>
</div>
  • Next we have to decide the size of aur google map canvas on which our map is to be displayed. We define the size of our canvas in the public-event.scss file which is located under styles/pages/public-event-scss.
.event-map > .g-map {
 height: 100%;
 width: 100%;
}
 
.event-map > .g-map > .g-map-canvas {
 height: 300px;
}
  • Lastly, we have to modify our event-map-test.js file. Here we perform integration test for our component. We create a dummy object with latitude, longitude, and locationName in it and pass it to our component and then check if it renders correctly or not.
import { test } from 'ember-qunit';

import moduleForComponent from 'open-event-frontend/tests/helpers/component-helper';

import hbs from 'htmlbars-inline-precompile';

moduleForComponent('public/event-map', 'Integration | Component | public/event map');

let event = Object.create({ latitude: 37.7833, longitude: -122.4167, locationName: 'Sample event location address' });

test('it renders', function(assert) {

  this.set('event', event);

  this.render(hbs `{{public/event-map event=event}}`);

  assert.equal(this.$('.address p').text(), 'Sample event location address');

});

Now use ember s command in our ember cli and visit localhost:4200

Then open a event at the end we see our Google map integrated.

Use ember test –server command to check if all our tests (integration, acceptance, etc.) are passed.

 

Find out more at – ember-g-map