Angular Konami Code
Tags: angular, konami, typescript, unittest
Published: 2021-09-07 12:57:17

---
title: Angular Konami Code
published: true
description: I've had the pleasure of working on several projects where small easter eggs were allowed. In the most recent, I build an Angular Directive, created a Module, and actually built Unit Tests.
tags: Angular, Konami, TypeScript, UnitTest
series: Angular
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0920yi5rfdjn8jja74lq.png
---

I've had the pleasure of working on several projects where small easter eggs were allowed.

In the most recent, I build an Angular Directive, created a Module, and actually built Unit Tests.

Here's the code.

## The Module

File: `konami.module.ts`

```typescript
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { KonamiDirective } from './konami.directive';
​
export * from './konami.directive';
​
@NgModule({
  imports: [ CommonModule ],
  declarations: [ KonamiDirective ],
  exports: [ KonamiDirective ]
})
export class KonamiModule {
  static forRoot(): ModuleWithProviders<KonamiModule> {
    return {
      ngModule: KonamiModule
    };
  }
}
```

Clearly, this is in the same folder as the Directive.

## The Directive

File: `konami.directive.ts`

```typescript
import { Directive, EventEmitter, HostListener, Output } from '@angular/core';
​
@Directive({
  selector: '[konami]'
})
export class KonamiDirective {
​
  @Output() private konami: EventEmitter<void>;
​
  private sequence: string[];
​
  private konamiCode: string[];
​
  constructor() {
    this.konami = new EventEmitter<void>();
    this.sequence = [];
    this.konamiCode = [
      'arrowup', 'arrowup',
      'arrowdown', 'arrowdown',
      'arrowleft', 'arrowright',
      'arrowleft', 'arrowright',
      'b', 'a'
    ];
  }
​
  @HostListener('window:keydown', ['$event'])
  handleKeyboardEvent(event: KeyboardEvent) {
    if (event.key) {
      this.sequence.push(event.key.toLowerCase());
​
      if (this.sequence.length > this.konamiCode.length) {
        this.sequence.shift();
      }
​
      if (this.isKonamiCode()) {
        this.konami.emit();
      }
    }
  }
​
  isKonamiCode(): boolean {
    return this.konamiCode.every((code: string, index: number) => code === this.sequence[index]);
  }
}
```

And, all this gets tested ...

## The Unit Tests

File: `konami.directive.spec.ts`

```typescript
import { KonamiDirective } from './konami.directive';
​
describe('Konami2irective', () => {
  let directive;
​
  beforeEach(() => {
    directive = new KonamiDirective();
  });
​
  afterEach(() => {
    directive = null;
  });
​
  it('should create an instance', () => {
    expect(directive).toBeTruthy();
  });
​
  it('expects "handleKeyboardEvent" to add keydown to sequence', () => {
    spyOn(directive.konami, 'emit').and.stub();
    spyOn(directive, 'isKonamiCode').and.callThrough();
    const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowUp' });
    directive.sequence = [];
​
    directive.handleKeyboardEvent(keyEvent);
    expect(directive.sequence).toEqual([ 'arrowup' ]);
    expect(directive.isKonamiCode).toHaveBeenCalled();
    expect(directive.konami.emit).not.toHaveBeenCalled();
  });
​
  it('expects "handleKeyboardEvent" to trigger a konami emit', () => {
    spyOn(directive.konami, 'emit').and.stub();
    spyOn(directive, 'isKonamiCode').and.callThrough();
    const keyEvent = new KeyboardEvent('keydown', { key: 'A' });
    directive.sequence = ['arrowup', 'arrowup', 'arrowdown', 'arrowdown', 'arrowleft', 'arrowright', 'arrowleft', 'arrowright', 'b'];
​
    directive.handleKeyboardEvent(keyEvent);
    expect(directive.isKonamiCode).toHaveBeenCalled();
    expect(directive.konami.emit).toHaveBeenCalled();
  });
​
  it('expects "handleKeyboardEvent" to not work if event had no key detail', () => {
    spyOn(directive.konami, 'emit').and.stub();
    spyOn(directive, 'isKonamiCode').and.callThrough();
    const keyEvent = new KeyboardEvent('keydown', { key: '' });
    directive.sequence = ['arrowup', 'arrowup', 'arrowdown', 'arrowdown', 'arrowleft', 'arrowright', 'arrowleft', 'arrowright', 'a'];
​
    directive.handleKeyboardEvent(keyEvent);
    expect(directive.isKonamiCode).not.toHaveBeenCalled();
    expect(directive.konami.emit).not.toHaveBeenCalled();
  });
​
  it('expects "handleKeyboardEvent" to add to the sequence, removing from the front', () => {
    spyOn(directive.konami, 'emit').and.stub();
    spyOn(directive, 'isKonamiCode').and.callThrough();
    directive.sequence = ['arrowup', 'arrowup', 'arrowdown', 'arrowdown', 'arrowleft', 'arrowright', 'arrowleft', 'arrowright', 'b', 'a'];
    const result =  ['arrowup', 'arrowdown', 'arrowdown', 'arrowleft', 'arrowright', 'arrowleft', 'arrowright', 'b', 'a', 'c'];
    const keyEvent = new KeyboardEvent('keydown', { key: 'C' });
​
    directive.handleKeyboardEvent(keyEvent);
    expect(directive.sequence).toEqual(result);
  });
​
  it('expects "isKonamiCode" to return true with a correct sequence', () => {
    spyOn(directive.konami, 'emit').and.stub();
    spyOn(directive, 'isKonamiCode').and.callThrough();
    directive.sequence = ['arrowup', 'arrowup', 'arrowdown', 'arrowdown', 'arrowleft', 'arrowright', 'arrowleft', 'arrowright', 'b', 'a'];
​
    expect(directive.isKonamiCode()).toEqual(true);
  });
​
});
```

Now, with all this in place it needs to be added to the application.

## Adding To Angular

File: `app.module.ts`

```typescript
import { KonamiModule } from '@shared/konami/konami.module';

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    KonamiModule,
    ...
  ],
  providers: [],
  bootstrap: [
    ...
  ]
})
export class AppModule { } 
```

## The Implementation

On a button or div, add the following ...

```typescript
(konami)="openEasterEgg()"
```

Clearly, you would want the `openEasterEgg` function to do something. I generally have it open a Modal with some reference to the design team.

Enjoy.