Angular Konami Code

bob.ts - Sep 7 '21 - - Dev Community

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

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
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

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

The Directive

File: konami.directive.ts

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]);
  }
}
Enter fullscreen mode Exit fullscreen mode

And, all this gets tested ...

The Unit Tests

File: konami.directive.spec.ts

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);
  });

});
Enter fullscreen mode Exit fullscreen mode

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

Adding To Angular

File: app.module.ts

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

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    KonamiModule,
    ...
  ],
  providers: [],
  bootstrap: [
    ...
  ]
})
export class AppModule { } 
Enter fullscreen mode Exit fullscreen mode

The Implementation

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

(konami)="openEasterEgg()"
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .