Category Archives: Programming

Anything related to programming or code

Why is toggling a search box in a header so difficult in Angular 2?

In Angular 2 I have the following pseduocode…

...

As you can see I have a header with a search form container that has the class ‘visible’ when the variable ‘showSearch’ is true, and ‘invisible’ otherwise. As a component by itself with this html, this works fine. When the user clicks the search icon showSearch is set to true and the form shows.

Now the issue comes when I want to hide this container again. I want to set showSearch to false and thus hide the container when the user clicks anywhere that isn’t the header (ie. the body of the page).
Now in jQuery, this would be easy. On click of the body, you would find the element with ‘.searchContainer’ and removeClass(‘visible’).addClass(‘invisible’) right? Easy.

Except this is Angular 2. We don’t have jQuery (or want to use it) here. Normally what I would do in this case, is to have something like this in the parent component that contains the header component:



..

That’s the HTML. I pass the showSearch variable down from the parent to the child and toggle it with the parent right? That’s how normally it works. One way binding. (If the child needs to talk to the parent it uses event emitters instead).


@HostListener('click', ['$event'])
closeSearchBox(event) {
const toElement = event.toElement;
let insideHeader = false;
let node = toElement;
while (node != null && node.classList !== undefined) {
if (node.classList.contains('header-container')) {
insideHeader = true;
}
node = node.parentNode;
}

if (!insideHeader) {
const searchBox = this.elementRef.nativeElement.querySelector('.search-container');
const searchMenu = this.elementRef.nativeElement.querySelector('.header-menu-list');
if (searchBox.classList.contains('visible')) {
this.showSearch = false;
}
}
}

So this should work right? I have an HostListener on the parent (that’s really the whole body of my app) so if the user clicks anywhere it will check to see if the element is outside the header (because we dont want to close it if its inside the header), and close the search container if it is.

Problem is… it didn’t work. I don’t know why. I searched StackOverflow, Angular Docs, scoured Google etc nothing. I even tried using EventEmitter but thats usually only from child to parent and not parent to child. Two-way bindings? nope doesn’t work either. It *should* just work inherently as part of Angular’s single way binding. Maybe the HostListener is outside the Angular Zone and I can use ChangeDetectorRef to detectChanges? Nope. That didn’t work either.
I *could* use a service and just subscribe to that service from the child component … but thats kind of overkill for ONE variable change to toggle a search box.

So finally. I had to make a solution that is not so elegant, and doesn’t use any variables or bindings. But it works.

So I took a jQuery-like approach of just toggling the class manually with ‘elementRef.nativeElement.classList’ instead. Its not as pretty as using bindings, but at least this way it works.


toggleSearchHeader(showHeader) {

if (showHeader) {
this.searchHeaderMenu.nativeElement.classList.remove('visible');
this.searchHeaderMenu.nativeElement.classList.add('invisible');
this.searchHeaderBox.nativeElement.classList.remove('invisible');
this.searchHeaderBox.nativeElement.classList.add('visible');
} else {
this.searchHeaderMenu.nativeElement.classList.remove('invisible');
this.searchHeaderMenu.nativeElement.classList.add('visible');
this.searchHeaderBox.nativeElement.classList.remove('visible');
this.searchHeaderBox.nativeElement.classList.add('invisible');
}

}

I have to substitute changing one variable showSearch with a whole method that does janky DOM crap and..


@HostListener('click', ['$event'])
closeSearchBox(event) {
const toElement = event.toElement;
let insideHeader = false;
let node = toElement;
while (node != null && node.classList !== undefined) {
if (node.classList.contains('header-container')) {
insideHeader = true;
}
node = node.parentNode;
}

if (!insideHeader) {
const searchBox = this.elementRef.nativeElement.querySelector('.search-container');
const searchMenu = this.elementRef.nativeElement.querySelector('.header-menu-list');
if (searchBox.classList.contains('visible')) {
searchBox.classList.remove('visible');
searchBox.classList.add('invisible');
searchMenu.classList.remove('invisible');
searchMenu.classList.add('visible');
}
}
}

So yeah as you can see those are the changes I needed to get it to work. For some reason, using a variable inside `HostListener` and passing that down to the child component doesn’t reflect the bindings. So I have to use a more archaic way instead. Not only that but its much more difficult to test with unit tests now that it relies on the DOM instead of an Angular variable. Bleh. If anyone knows why this is, let me know please! comment on this post..

Unit Tests in Angular 2

Recently at my new company, Spigit, I’ve started on working on unit testing. Now, previously I’ve done unit testing and end to end testing using NighwatchJS at Walmart Labs.
And now with my new project, I have to use Angular 2.

After working with Angular 2 for a few months now and ReactJS last year, my feelings towards the two are: ReactJS is more flexible and lightweight, and Angular 2 comes with more out of the box features. It’s kind of like the old BackboneJS vs Angular 1 argument. Do you want a more lightweight, flexible framework, or do you want to go all in with one?

With ReactJS, you only have the “view” layer and you still have to use Flux or Redux to actually make service or API calls. Then you have to add Flow for type checking.
With Angular 2, you have that all out of the box with a comprehensive library + TypeScript which has built in type checking and is ES6 natively (but not exactly the same as ES6 as I found out later on). But Angular 2 is more heavy and you might not be using its full capability, so it’s not as flexible as ReactJS. It’s just like how Angular 1.x provided everything out of the box whereas BackboneJS required you to use UnderscoreJS, jQuery etc on top of it (which some companies expanded upon more with MarionetteJS, HandlebarsJS, EpoxyJS, ThoraxJS, Lodash etc adding various capabilities onto Backbone).

Out of the two, ReactJS has a higher learning curve I feel. Having the markup in the JS files (JSX) and dealing with the component lifecycle and passing in props is a very different way of coding than the past.
Angular 2 (and Angular 4, etc) is also very different than AngularJS (that’s Angular 1.x btw), but its not quite the same chasm of difference that React is. AngularJS devs can migrate to Angular 2 fairly easily as there’s a guide for it, it still uses templates just like Backbone or AngularJS and you still have things like directives and pipes, but now everything is a component (let’s face it we live in an ES6 world now and you should be familiar with ES6 and dealing with importing/exporting components instead of the old MVC structure). Other AngularJS functions like `$scope.apply` have equivalents in Angular 2 like `changeDetectorRef.detectChanges()` But still out of the two new age popular frameworks, Angular 2 is easier to pick up. I technically prefer VueJS to either of them but it’s not quite as popular yet.

So my last 6 months with Walmart Labs I was mostly doing end to end testing using the NightwatchJS framework which uses the Selenium driver to automate browser testing. It puts less pressure on manual QA testing. We did testing on four major browsers: IE11, Firefox (last 3 versions), Chrome (last 3 versions), and Safari. We found some quirks with testing in certain browsers. For example on IE11, we would have to use clearValue twice to actually clear a value.


// For IE11
client.clearValue('input[type=text]');
client.clearValue('input[type=text]');

Which was weird, and also oddly enough on Firefox, clicking a button wouldn’t work unless the button was actually in the viewport. So we have to scroll down to it first.


// For Firefox
client.moveToElement('#main', 10, 10);
client.click("#main ul li a.first");

Very odd issues. But most people use Chrome as the standard and we only noticed the other failures since we integrated our CI with Admiral and SauceLabs.

Out of E2E testing and unit testing, Both are important, but when choosing which one to start out with, I feel unit testing is the more fundamental one to start out with. If you have a great QA team already, then you can make do without E2E testing since all it really does is automate integration testing. Unit tests also help catch regressions but on a more fundamental component level, whereas E2E catches regressions on the UI level. QAs can actually write E2E tests. But QAs usually don’t write unit tests; that’s the developers job since they know their own code the best.

ReactJS uses Mocha for unit testing (and Sinon for spies), while Angular uses Jasmine which come with their own spies. So starting out on Angular 2’s testing guide, its a big long ass intimidating page to read, and there’s tons of testing functions in there. I recommend reading the first half as the latter half are for very niche scenarios. There’s also a great plnkr link at the bottom of the page which contains ALL the unit tests in their whole, and I recommend looking at that because just showing code snippets is really not good enough. Sometimes you might be missing imports or stubs or declarations that you don’t see in the code snippets.

One of issues I ran into on the guide is using stubs vs mock classes. When we are testing components, we have to choose between using one of those if we have a function that reaches out to a service. We don’t want to call the real service. So, if we want more flexibility we can use a mock class, but the problem is Angular’s example doesn’t really work for me… when I put in the mock class as a provider using `useClass` and then using `overrideComponents`, Angular complains about it in my IDE.. I posted a question on stack overflow about it, but no one seems to reply.
So, I’m stuck using stubs instead, which is fine if the component doesn’t do too many service calls, but you lose out on the power of mock classes, but oh well. With Stubs we have to use `useValue` instead. Oh, and another gotcha I encountered… Jasmine spies by default only intercept the function but don’t call through to the real function. You have to use `spyOn(foo, ‘bar’).and.callThrough();` to actually go into the real function, which is useful when you have multiple spies on functions that call other functions.

Now I know there’s a lot of Angular 2 testing articles out there… and a lot of them have different strategies.. this is the one that worked for me…


// In addition to importing all the modules and services / providers you are using here, you need the core libraries...
import { fakeAsync, tick, async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DebugElement, CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';


describe('HeaderComponent', () => {

let comp: HeaderComponent;
let fixture: ComponentFixture;
let sessionServiceStub: any;
let utilsServiceStub: any;

beforeEach(async(() => {
utilsServiceStub = {
getPageName: jasmine.createSpy('getPageName').and.callFake(
() => ''
)
};
sessionServiceStub = {
getUserInfo: jasmine.createSpy('getUserInfo').and.callFake(() => {
return {
displayName: 'Jack'
};
}),
getSiteInfo: jasmine.createSpy('getSiteInfo').and.callFake(
() => Promise.resolve(true).then(() => {})
)
};
TestBed.configureTestingModule({
declarations: [HeaderComponent],
imports: [TranslateModule.forRoot()],
providers: [
{ provide: SessionService, useValue: sessionServiceStub },
{ provide: UtilsService, useValue: utilsServiceStub }
],
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(HeaderComponent);
comp = fixture.componentInstance;
comp.nav = [
{
pageName: 'Home',
tabLabel: 'tablabels.home'
},
{
pageName: 'PostIdea',
tabLabel: 'tablabels.post_idea'
},
{
pageName: 'ViewIdeas',
tabLabel: 'tablabels.view_ideas'
}
];
});

describe('component onInit', () => {
it('can instantiate it', () => {
expect(comp).not.toBeNull();
});

it('should init the header logo', () => {
const headerLogoEl = fixture.debugElement.query(By.css('.header-logo'));
expect(headerLogoEl.nativeElement.textContent).not.toBeNull();
});

it('should init the header menu', () => {
const headerMenuEl = fixture.debugElement.query(By.css('.header-menu'));
expect(headerMenuEl.nativeElement.textContent).not.toBeNull();
});

it('should init the right services on init', () => {
fixture.detectChanges();
expect(utilsServiceStub.getPageName.calls.count()).toBe(1);
expect(sessionServiceStub.getUserInfo.calls.count()).toBe(1);
expect(sessionServiceStub.getSiteInfo.calls.count()).toBe(1);
});
});

describe('buildMenu()', () => {
it('should init the menu holder', () => {
const navBarEl = fixture.debugElement.query(By.css('.navbar-spigit'));
spyOn(comp, 'buildMenu');
navBarEl.triggerEventHandler('showMoreMenu', null);
expect(comp.buildMenu).toHaveBeenCalled();
const menuHolder = fixture.debugElement.query(By.css('.more-menu-holder'));
const menuHolderContent = menuHolder.nativeElement.textContent;
expect(menuHolderContent).not.toBeNull();
});

it('should show dropdown menu when moreMenuConfig is visible', () => {
let dropdownMenu = fixture.debugElement.query(By.css('.dropdown-menu'));
expect(dropdownMenu).toBeNull();
comp.moreMenuConfig = {
visible : true,
mouseOutIntervalTime : 300,
mouseOutInterval: null,
moreMenuButtonVisible: true
};
// dropdown menu should be visible now
fixture.detectChanges();
dropdownMenu = fixture.debugElement.query(By.css('.dropdown-menu'));
expect(dropdownMenu).not.toBeNull();
});
});
});

and if you’re using Angular Bootstrap.. it actually does async stuff under the hood so you have to be careful about selecting DOM elements if they are using Bootstrap…


it('should call onScroll when scrolling on menu', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => { // wait for promise to return
fixture.detectChanges();
const menuEl = fixture.debugElement.query(By.css('.dropdown-menu'));
menuEl.triggerEventHandler('scroll', {
target: {
scrollHeight: 100,
scrollTop: 0,
clientHeight: 0
}
});
expect(comp.onScroll).toHaveBeenCalled();
expect(comp.getCommunities).toHaveBeenCalled();
})
}));

And sometimes when you are testing service calls, you also want to test for functions or code that executes in the callback of the promise as well… then you need `fakeAsync` for the job:


it('should get mini profile', fakeAsync(() => {
nameElmt.triggerEventHandler('mouseover', null); // mouseover triggers async service call
fixture.detectChanges();
tick(); // this essentially forwards the promise to the 'then' callback function
expect(userServiceStub.getMiniProfile.calls.count()).toBe(1);
}));

That’s just some sample code for testing components… now for testing services it gets more difficult since the Angular 2 testing guide doesn’t explain this very thoroughly… this is how I did it


import * as data from './data.json';

Now in TypeScript we can’t actually import json without it complaining about it missing a module definition.. unlike in native ES6. So we have to use a workaround hack .. we have to create another separate file in the same directory as the json file with the same name (including .json) so we have a file called `data.json.ts` and inside it the only content we have is

export default '';

And that’s it! Now the importing works. It’s weird but yes we have to do that until TypeScript natively supports importing JSON.

then in the test file…


describe('PageService', () => {

let utilsServiceStub: any;
const mockResponse:any = data; // Typescript will not recognize anything in the json data unless we force a type on it

beforeEach(async(() => {
utilsServiceStub = {
getPageName: jasmine.createSpy('getPageName').and.callFake((param: string) => '')
};
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
PageService,
MockBackend,
{ provide: UtilsService, useValue: utilsServiceStub },
{ provide: XHRBackend, useClass: MockBackend }
],
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA]
}).compileComponents();
}));

describe('getPagePromise()', () => {
it ('should return appropiate response', async(
inject([PageService, MockBackend], (pageService, mockBackend) => {

mockBackend.connections.subscribe((connection: MockConnection) => {
connection.mockRespond(new Response(new ResponseOptions({
body: JSON.stringify(mockResponse)
})));
});

pageService.getPagePromise().then((pageData) => {
expect(utilsServiceStub.getPageName.calls.count()).toBe(1, 'getPageName called once');
expect(pageData.ideaStages.length).toBe(6);
expect(pageData.numLifecycleStages).toBe(7);
expect(pageData.creatorName).toEqual('Viviana Arandia');
expect(pageData.ideaTitle).toEqual('ONE POPULAR STAR WARS PLANET THAT ALMOST SHOWED UP IN ROGUE ONE');
expect(pageData.hasViewed).toBe(false);
});

})));
});
});

Sometimes … there is a function that deals with DOM and things become very difficult to test with native functions like setTimeOut() for example.


keyPressHandler(event): void {
if (event.keyCode === 13 && this.searchInput.nativeElement.value) {
this.submitSearch();
}
}

clearMoreBtnTimeout() {
clearInterval(this.moreMenuConfig.mouseOutInterval);
}

In this example, its hard to test because we can’t simulate native events easily or native DOM elements that easily. Instead what we can do is pass in an optional test variable that denotes its being used for a test, and that way we can reach better code coverage without having to deal with potentially nasty DOM/event simulations.


keyPressHandler(event, test?): void {
if (event.keyCode === 13 && this.searchInput.nativeElement.value) {
this.submitSearch();
} else if (test) {
this.submitSearch();
}
}

clearMoreBtnTimeout(test?) {
// clear morebutton over timeout
if (!test) {
clearInterval(this.moreMenuConfig.mouseOutInterval);
} else {
this.moreMenuConfig.mouseOutInterval = 0;
}
}

Ok, now we can test it like this:


describe('clearMoreBtnTimeout()', () => {

it('should set mouseOutInterval to 0', () => {
component.clearMoreBtnTimeout(true);
expect(component.moreMenuConfig.mouseOutInterval).toEqual(0);
});

});

describe('keyPressHandler()', () => {

it('should call submitSearch', () => {
spyOn(component, 'submitSearch');
component.keyPressHandler({}, true);
expect(component.submitSearch).toHaveBeenCalled();
});

});

And just like that, we can up our code coverage for those pesky sections of code that were hard to unit test before.

So those are my component and service tests… let me know if that helps anyone! the existing Angular 2 unit testing articles are very confusing and contradictory for me so.. this is what worked for me.

Integrating payments into a web application

If you ever make a web application, and you want it to make money, sooner or later you have to deal with integrating a payment system. Here at BitTorrent Bundles, I’ve been tasked with integrating a payment system with our product, and it’s been hell to deal with.
Once you deal with payments, you have to deal with legal issues, with PCI, with SSL and security, with international currencies and conversions and all this baggage that comes with it.

What kind of third party payment systems are out there?
Well, making our own payment system is definitely a major hassle since we would have to deal with PCI compliance, so a third party system is definitely the way to go, since we wouldn’t have to store any credit card information and the legal ramifications with that. Who do we have?

1) Paypal – which is the oldest and has the most ‘legacy’ API if you will, but is generally the most accepted among consumers, especially consumers without a credit card.
2) Balanced Payments – haven’t vetted this one enough yet, but the API Docs leave a lot to be desired.
3) Stripe – Stripe is generally pretty good, has excellent API Docs and examples, and makes it easy to integrate, but is not a ‘white label’ solution.
4) BrainTree – similar to Balanced Payments in that they let you have a white label solution, but has more thorough API documentation.

The one I’ve started with is Stripe mainly because of their ease of integration – just embed Stripe’s JS scripts in your page, make the API call to Stripe, get the token and send it to your database, thats it. In addition they have some of the most excellent documentation I’ve seen from any service.

There’s still a lot of challenges to go through when implementing this however, the questions in particular being:

  • How do Publishers sign up? How do we get their bank account information?
  • How do we take a cut of the payment and still distribute funds to them?
  • How do we deal with currency conversion and international customers?
  • How do users redownload torrents?
  • How do we deal with SSL issues since http://bundles.bittorrent.com is not SSL certed?

These challenges are new to me, and people are still uncomfortable with the idea of paying for something on BitTorrent, which people have used for years to pirate content for free. But trust me, this is the first step to get BitTorrent more validation as a legitimate user created content peer the likes of Youtube, or iTunes. First, I can address these problems as follows:

How do Publishers sign up? Stripe connect has a surprisingly robust implementation that allows us to get the publisher to enter their user information through Stripe and post back to us with a token that allows us to use their Stripe access token to create their bundle. This means we can bypass Bank account information, something we can’t do if we integrate with BrainTree or BalancedPayments.

How do we take a cut of the payment and still distribute funds to them? This is tricky. You can either create a “master” Stripe account akin to a BrainTree marketplace merchant account and use that publishable key and secret key to create charges and customers on behalf of the publisher, and pass in the application fee at charge time, and use the Transfer API to distribute the funds to publishers. OR use each publisher’s publishable and secret key, and the payments go directly to each publisher; this is the intent of Stripe Connect.

International payments: This is also very tricky. According to Stripe they do support 130 currencies but the Stripe account has to be settled under one currency. This is the same case for other payment services as well. The only way to get multiple sub-accounts settled in multiple currencies is to use Stripe connect for each publisher separately, or the BrainTree equivalent, Partners API. But if we have a master account, then it has to be that one currency.

How do users redownload torrents?: We were thinking of storing the users email and authenticating against that. But instead, I figured we can use the confirmation email to validate the email itself, and provide a download link with a confirmation token attached to validate the torrent download. The download link and the token can either expire or not.

SSL issues: We are looking to SSL cert the entire Bundles site, including localhost, which uTorrent needs to download torrents from the web, especially over SSL. This also avoids the issue of having to open a popup window from the page to avoid Man in the middle attacks and have a way of communicating to the parent window.

Here’s something cool I learned while researching this: We normally want to use HTML5 PostMessage to communicate between windows, but IE8-10 doesn’t support postmessage between popup windows, but the child window has direct access to the parent window through window.opener, even in IE8!! But we have to attach a global window method for this to work, so outside of IE using postmessage is still preferable like so:

//In your parent window
$(window).on('message', _.bind(this.postMessageReceiver, this));
if (isIE) {
   window.postMessageReceiver = _.bind(function () {
       //do redirect stuff here 
   }, this);
}

//in your child window
  function postMessageToParent () {
        if (isIE) {
            window.opener.postMessageReceiver();
        } else {
            var o = window.opener;
            o.postMessage('Event has happened', '*');
        }
    }