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 }; } }
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]); } }
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); }); });
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 { }
The Implementation
On a button or div, add the following ...
(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.
Top comments (0)