Skip to content

Commit

Permalink
chore: Add tests for utils (#31)
Browse files Browse the repository at this point in the history
* chore: add logger tests

* chore: add linked list queue

* chore: add event emitter tests

* chore: move mappers

* chore: add interceptors storage tests

* chore: add store tests

---------

Co-authored-by: Dzianis Dashkevich <ddashkevich@brightcove.com>
  • Loading branch information
dzianis-dashkevich and Dzianis Dashkevich authored Oct 7, 2024
1 parent 1b98969 commit 5850096
Show file tree
Hide file tree
Showing 20 changed files with 610 additions and 22 deletions.
1 change: 1 addition & 0 deletions packages/playback/src/lib/consts/interceptor-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
// since they can be used as values they should not be in the types folder
export enum InterceptorType {
NetworkRequest = 'NetworkRequest',
HlsPlaylistParse = 'HlsPlaylistParse',
}
2 changes: 1 addition & 1 deletion packages/playback/src/lib/network/network-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
import { NetworkRequestWithChunkHandler, NetworkRequestWithMapper } from './network-request';
import type { PlayerNetworkConfiguration } from '../types/configuration.declarations';
import type { IEventEmitter } from '../types/event-emitter.declarations';
import type { NetworkEventMap } from '../types/event-type-to-event-map.declarations';
import type { NetworkEventMap } from '../types/mappers/event-type-to-event-map.declarations';

export interface NetworkManagerDependencies {
logger: ILogger;
Expand Down
2 changes: 1 addition & 1 deletion packages/playback/src/lib/network/network-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from './network-manager-errors';
import type { ILogger } from '../types/logger.declarations';
import type { IEventEmitter } from '../types/event-emitter.declarations';
import type { NetworkEventMap } from '../types/event-type-to-event-map.declarations';
import type { NetworkEventMap } from '../types/mappers/event-type-to-event-map.declarations';
import {
NetworkRequestAttemptStartedEvent,
NetworkRequestAttemptFailedEvent,
Expand Down
2 changes: 1 addition & 1 deletion packages/playback/src/lib/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { PlayerConfiguration } from './types/configuration.declarations';
import type { IStore } from './types/store.declarations';
import type { DeepPartial } from './types/utility.declarations';
import type { EventListener, IEventEmitter } from './types/event-emitter.declarations';
import type { EventTypeToEventMap } from './types/event-type-to-event-map.declarations';
import type { EventTypeToEventMap } from './types/mappers/event-type-to-event-map.declarations';
import { PlayerEventType } from './consts/events';
import {
ConfigurationChangedEvent,
Expand Down
4 changes: 2 additions & 2 deletions packages/playback/src/lib/service-locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import type { IInterceptorsStorage } from './types/interceptors.declarations';
import type { PlayerConfiguration } from './types/configuration.declarations';
import type { IStore } from './types/store.declarations';
import type { IEventEmitter } from './types/event-emitter.declarations';
import type { EventTypeToEventMap } from './types/event-type-to-event-map.declarations';
import type { EventTypeToEventMap } from './types/mappers/event-type-to-event-map.declarations';
import type { IEnvCapabilitiesProvider } from './types/env-capabilities.declarations';
import type { INetworkManager } from './types/network.declarations';
import type { InterceptorTypeToInterceptorMap } from './types/interceptor-type-to-interceptor-map.declarations';
import type { InterceptorTypeToInterceptorMap } from './types/mappers/interceptor-type-to-interceptor-map.declarations';
import type { NetworkManagerDependencies } from './network/network-manager';

// Implementations
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { InterceptorType } from '../consts/interceptor-type';
import type { InterceptorTypeToInterceptorMap } from './interceptor-type-to-interceptor-map.declarations';
import type { InterceptorTypeToInterceptorMap } from './mappers/interceptor-type-to-interceptor-map.declarations';

export interface IInterceptorsStorage {
addInterceptor<K extends InterceptorType>(interceptorType: K, interceptor: InterceptorTypeToInterceptorMap[K]): void;
Expand Down
2 changes: 2 additions & 0 deletions packages/playback/src/lib/types/logger.declarations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { LoggerLevel } from '../consts/logger-level';

export interface ILogger {
readonly label: string;

createSubLogger(subLabel: string): ILogger;

setLoggerLevel(level: LoggerLevel): void;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// mapping for types purposes
import type { PlayerEventType } from '../consts/events';
import type { PlayerEventType } from '../../consts/events';
import type {
ConfigurationChangedEvent,
LoggerLevelChangedEvent,
MutedStatusChangedEvent,
VolumeChangedEvent,
PlayerErrorEvent,
} from '../events/player-events';
} from '../../events/player-events';
import type {
NetworkRequestAttemptCompletedSuccessfullyEvent,
NetworkRequestAttemptCompletedUnsuccessfullyEvent,
NetworkRequestAttemptFailedEvent,
NetworkRequestAttemptStartedEvent,
} from '../events/network-events';
} from '../../events/network-events';

export interface NetworkEventMap {
[PlayerEventType.NetworkRequestAttemptStarted]: NetworkRequestAttemptStartedEvent;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { InterceptorType } from '../../consts/interceptor-type';

export interface InterceptorTypeToInterceptorMap {
[InterceptorType.NetworkRequest]: (request: Request) => Promise<Request>;
[InterceptorType.HlsPlaylistParse]: (playlist: Uint8Array) => Promise<Uint8Array>;
}
2 changes: 1 addition & 1 deletion packages/playback/src/lib/types/network.declarations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { RequestType } from '../consts/request-type';
import type { NetworkConfiguration, PlayerNetworkConfiguration } from './configuration.declarations';
import type { InterceptorTypeToInterceptorMap } from './interceptor-type-to-interceptor-map.declarations';
import type { InterceptorTypeToInterceptorMap } from './mappers/interceptor-type-to-interceptor-map.declarations';
import type { InterceptorType } from '../consts/interceptor-type';
import type { AttemptInfo } from './retry.declarations';

Expand Down
4 changes: 2 additions & 2 deletions packages/playback/src/lib/utils/interceptors-storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { InterceptorType } from '../consts/interceptor-type';
import type { InterceptorTypeToInterceptorMap } from '../types/interceptor-type-to-interceptor-map.declarations';
import type { InterceptorTypeToInterceptorMap } from '../types/mappers/interceptor-type-to-interceptor-map.declarations';
import type { IInterceptorsStorage } from '../types/interceptors.declarations';

export class InterceptorsStorage implements IInterceptorsStorage {
Expand All @@ -17,7 +17,7 @@ export class InterceptorsStorage implements IInterceptorsStorage {
}

public getInterceptorsSet<K extends InterceptorType>(interceptorType: K): Set<InterceptorTypeToInterceptorMap[K]> {
return new Set(this.storage_.get(interceptorType) ?? []);
return new Set((this.storage_.get(interceptorType) as Set<InterceptorTypeToInterceptorMap[K]>) ?? []);
}

public removeInterceptor<K extends InterceptorType>(
Expand Down
65 changes: 65 additions & 0 deletions packages/playback/src/lib/utils/linked-list-queue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
class LinkedListQueueNode<T> {
public value: T;
public next: LinkedListQueueNode<T> | null = null;

public constructor(value: T) {
this.value = value;
}
}

export class LinkedListQueue<T> {
private head_: LinkedListQueueNode<T> | null = null;
private tail_: LinkedListQueueNode<T> | null = null;
private length_: number = 0;

public enqueue(value: T): void {
const newNode = new LinkedListQueueNode(value);

if (this.tail_) {
this.tail_.next = newNode;
}

this.tail_ = newNode;

if (!this.head_) {
this.head_ = newNode;
}

this.length_++;
}

public dequeue(): T | null {
if (!this.head_) {
return null;
}

const value = this.head_.value;
this.head_ = this.head_.next;

if (!this.head_) {
this.tail_ = null;
}

this.length_--;
return value;
}

public empty(): void {
this.head_ = null;
this.tail_ = null;
this.length_ = 0;
}

public get peek(): T | null {
return this.head_ ? this.head_.value : null;
}

// Get size of the queue
public get size(): number {
return this.length_;
}

public get isEmpty(): boolean {
return this.length_ === 0;
}
}
9 changes: 5 additions & 4 deletions packages/playback/src/lib/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,27 @@ export interface LoggerDependencies {
}

export class Logger implements ILogger {
public readonly label: string;

private readonly console_: Console;
private readonly label_: string;
private readonly delimiter_: string;

private level_: LoggerLevel = LoggerLevel.Debug;

private get cLabel_(): string {
return `%c${this.label_}`;
return `%c${this.label}`;
}

public constructor(dependencies: LoggerDependencies) {
this.console_ = dependencies.console;
this.label_ = dependencies.label;
this.label = dependencies.label;
this.delimiter_ = dependencies.delimiter;
}

public createSubLogger(subLabel: string): Logger {
return new Logger({
console: this.console_,
label: `${this.label_} ${this.delimiter_} ${subLabel}`,
label: `${this.label} ${this.delimiter_} ${subLabel}`,
delimiter: this.delimiter_,
});
}
Expand Down
2 changes: 1 addition & 1 deletion packages/playback/test/network/network-request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
IRequestPayloadWithMapper,
} from '../../src/lib/types/network.declarations';
import type { IEventEmitter } from '../../src/lib/types/event-emitter.declarations';
import type { NetworkEventMap } from '../../src/lib/types/event-type-to-event-map.declarations';
import type { NetworkEventMap } from '../../src/lib/types/mappers/event-type-to-event-map.declarations';
import type { NetworkRequestDependencies } from '../../src/lib/network/network-request';
import type { NetworkConfiguration } from '../../src/lib/types/configuration.declarations';

Expand Down
94 changes: 94 additions & 0 deletions packages/playback/test/utils/event-emitter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { EventEmitter } from '../../src/lib/utils/event-emitter';

describe('EventEmitter', () => {
let emitter: EventEmitter<{ testEvent: string; anotherEvent: string }>;

beforeEach(() => {
emitter = new EventEmitter();
});

it('should register an event listener when added', () => {
const listener = vi.fn();

emitter.addEventListener('testEvent', listener);
emitter.emitEvent({ type: 'testEvent' });

expect(listener).toHaveBeenNthCalledWith(1, { type: 'testEvent' });
});

it('should unregister an event listener successfully', () => {
const listener = vi.fn();

emitter.addEventListener('testEvent', listener);
emitter.removeEventListener('testEvent', listener);
emitter.emitEvent({ type: 'testEvent' });

expect(listener).not.toHaveBeenCalled();
});

it('should trigger all registered listeners when event is emitted', () => {
const listener1 = vi.fn();
const listener2 = vi.fn();

emitter.addEventListener('testEvent', listener1);
emitter.addEventListener('testEvent', listener2);
emitter.emitEvent({ type: 'testEvent' });

expect(listener1).toHaveBeenCalledTimes(1);
expect(listener2).toHaveBeenCalledTimes(1);
});

it('should register a listener that is triggered only once when using "once"', () => {
const listener = vi.fn();

emitter.once('testEvent', listener);
emitter.emitEvent({ type: 'testEvent' });
emitter.emitEvent({ type: 'testEvent' });

expect(listener).toHaveBeenCalledTimes(1);
});

it('should clear all listeners for a specific event when removed', () => {
const listener1 = vi.fn();
const listener2 = vi.fn();
const listener3 = vi.fn();

emitter.addEventListener('testEvent', listener1);
emitter.addEventListener('testEvent', listener2);
emitter.addEventListener('anotherEvent', listener3);

emitter.removeAllEventListenersFor('testEvent');
emitter.emitEvent({ type: 'testEvent' });
emitter.emitEvent({ type: 'anotherEvent' });

expect(listener1).not.toHaveBeenCalled();
expect(listener2).not.toHaveBeenCalled();
expect(listener3).toHaveBeenCalledTimes(1);
});

it('should clear all event listeners when removeAllEventListeners is called', () => {
const listener1 = vi.fn();
const listener2 = vi.fn();

emitter.addEventListener('testEvent', listener1);
emitter.addEventListener('anotherEvent', listener2);

emitter.removeAllEventListeners();
emitter.emitEvent({ type: 'testEvent' });
emitter.emitEvent({ type: 'anotherEvent' });

expect(listener1).not.toHaveBeenCalled();
expect(listener2).not.toHaveBeenCalled();
});

it('should register the same listener only once for the same event', () => {
const listener = vi.fn();

emitter.addEventListener('testEvent', listener);
emitter.addEventListener('testEvent', listener);
emitter.emitEvent({ type: 'testEvent' });

expect(listener).toHaveBeenCalledTimes(1);
});
});
Loading

0 comments on commit 5850096

Please sign in to comment.