Skip to content

Commit

Permalink
Source priority (#55)
Browse files Browse the repository at this point in the history
* #14 - Add source priority list in AvatarConfig. Read config file in avatar service constructor, check for a source priority list reorder sources constant based on order of config priority list.

* Reimplement custom avatar source order in refactored avatar.service.

* new comparison, ng guidelines, comment

* doc comments

* add tests scenarios

* Implement source priority order in refactored avatar service.

* unit tests for custom source priority order

* Remove debugger statement.

* refactor source priority

* refactor avatar source configuration

* delete covergae folder
  • Loading branch information
HaithemMosbahi authored Mar 25, 2019
1 parent 44791e7 commit 118a7e6
Show file tree
Hide file tree
Showing 9 changed files with 11,739 additions and 110 deletions.
11,455 changes: 11,455 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions projects/ngx-avatar/src/lib/avatar-config.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { AvatarConfigService } from './avatar-config.service';
import { AvatarSource } from './sources/avatar-source.enum';
import { AvatarConfig } from './avatar-config';
import { defaultSources, defaultColors } from './avatar.service';

describe('AvatarConfigService', () => {
describe('AvatarSources', () => {
it('should return the list of sources with the default order when the user provides an empty list of sources', () => {
const userConfig: AvatarConfig = { sourcePriorityOrder: [] };
const avatarConfigService = new AvatarConfigService(userConfig);

expect(avatarConfigService.getAvatarSources(defaultSources)).toEqual(
defaultSources
);
});

it('should return the list of sources with the default order when the user provides an unknown list of sources', () => {
const userConfig: AvatarConfig = {
sourcePriorityOrder: <any>['UNKOWN_SOURCE']
};
const avatarConfigService = new AvatarConfigService(userConfig);

expect(avatarConfigService.getAvatarSources(defaultSources)).toEqual(
defaultSources
);
});

it('should override the source priority order when the user provides a valid list of sources', () => {
const userConfig: AvatarConfig = {
sourcePriorityOrder: [AvatarSource.INITIALS, AvatarSource.TWITTER]
};
const avatarConfigService = new AvatarConfigService(userConfig);

const expectedSourcesOrder = [
AvatarSource.INITIALS,
AvatarSource.TWITTER,
AvatarSource.FACEBOOK,
AvatarSource.GOOGLE,
AvatarSource.VKONTAKTE,
AvatarSource.SKYPE,
AvatarSource.GRAVATAR,
AvatarSource.GITHUB,
AvatarSource.CUSTOM,
AvatarSource.VALUE
];
expect(avatarConfigService.getAvatarSources(defaultSources)).toEqual(
expectedSourcesOrder
);
});

it('should ignore redundant sources', () => {
const userConfig: AvatarConfig = {
sourcePriorityOrder: [AvatarSource.INITIALS, AvatarSource.INITIALS]
};
const avatarConfigService = new AvatarConfigService(userConfig);

const expectedSourcesOrder = [
AvatarSource.INITIALS,
AvatarSource.FACEBOOK,
AvatarSource.GOOGLE,
AvatarSource.TWITTER,
AvatarSource.VKONTAKTE,
AvatarSource.SKYPE,
AvatarSource.GRAVATAR,
AvatarSource.GITHUB,
AvatarSource.CUSTOM,
AvatarSource.VALUE
];
expect(avatarConfigService.getAvatarSources(defaultSources)).toEqual(
expectedSourcesOrder
);
});
});

describe('AvatarColors', () => {
it("should return the user's list of colors when provided in the avatar configuration", () => {
const userColors = ['#ccc', '#fff'];
const userConfig: AvatarConfig = {
colors: userColors
};

const avatarConfigService = new AvatarConfigService(userConfig);

expect(avatarConfigService.getAvatarColors(defaultColors)).toBe(
userColors
);
});

it('should return the default colors when no colors are provided in the avatar configuration', () => {
const avatarConfigService = new AvatarConfigService({});

expect(avatarConfigService.getAvatarColors(defaultColors)).toBe(
defaultColors
);
});
});
});
41 changes: 41 additions & 0 deletions projects/ngx-avatar/src/lib/avatar-config.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Injectable, Inject, Optional } from '@angular/core';

import { AvatarSource } from './sources/avatar-source.enum';
import { AVATAR_CONFIG } from './avatar-config.token';
import { AvatarConfig } from './avatar-config';

@Injectable()
export class AvatarConfigService {
constructor(
@Optional()
@Inject(AVATAR_CONFIG)
public userConfig: AvatarConfig
) {}

public getAvatarSources(defaultSources: AvatarSource[]): AvatarSource[] {
if (
this.userConfig.sourcePriorityOrder &&
this.userConfig.sourcePriorityOrder.length
) {
const uniqueSources = [...new Set(this.userConfig.sourcePriorityOrder)];
const validSources = uniqueSources.filter(source =>
defaultSources.includes(source)
);
return [
...validSources,
...defaultSources.filter(source => !validSources.includes(source))
];
}
return defaultSources;
}

public getAvatarColors(defaultColors: string[]): string[] {
return (
(this.userConfig &&
this.userConfig.colors &&
this.userConfig.colors.length &&
this.userConfig.colors) ||
defaultColors
);
}
}
13 changes: 12 additions & 1 deletion projects/ngx-avatar/src/lib/avatar-config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import { AvatarSource } from "./sources/avatar-source.enum";

/**
* Represents avatar configuration object
* Represents avatar configuration object.
*/
export interface AvatarConfig {

/**
* The avatars colors.
*/
colors?: string[];

/**
* The order in which the avatar sources will be used.
*/
sourcePriorityOrder?: AvatarSource[];
}
5 changes: 4 additions & 1 deletion projects/ngx-avatar/src/lib/avatar.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';

import { AvatarComponent } from './avatar.component';
import { SourceFactory } from './sources/source.factory';
import { AvatarService } from './avatar.service';
import { AvatarConfig } from './avatar-config';
import { AVATAR_CONFIG } from './avatar-config.token';
import { AvatarConfigService } from './avatar-config.service';

@NgModule({
imports: [
Expand All @@ -17,7 +19,8 @@ import { AVATAR_CONFIG } from './avatar-config.token';
],
providers: [
SourceFactory,
AvatarService
AvatarService,
AvatarConfigService
],
exports: [
AvatarComponent
Expand Down
179 changes: 102 additions & 77 deletions projects/ngx-avatar/src/lib/avatar.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,109 +1,134 @@
import { TestBed } from "@angular/core/testing";
import { TestBed } from '@angular/core/testing';
import {
HttpClientTestingModule,
HttpTestingController
} from "@angular/common/http/testing";

import { AvatarService } from "./avatar.service";
import { AVATAR_CONFIG } from "./avatar-config.token";
import { AvatarSource } from "./sources/avatar-source.enum";

describe("AvatarService", () => {
} from '@angular/common/http/testing';

import { AvatarService, defaultSources, defaultColors } from './avatar.service';
import { AvatarSource } from './sources/avatar-source.enum';
import { AvatarConfigService } from './avatar-config.service';

const avatarServiceCongigSpy = {
getAvatarSources: jasmine
.createSpy('avatarConfigService.getAvatarSources')
.and.returnValue(defaultSources),
getAvatarColors: jasmine
.createSpy('avatarConfigService.getAvatarColors')
.and.returnValue(defaultColors)
};

describe('AvatarService', () => {
let avatarService: AvatarService;
let httpMock: HttpTestingController;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [AvatarService, { provide: AVATAR_CONFIG, useValue: {} }]
});
describe('Avatar service with default configuration', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
AvatarService,
{ provide: AvatarConfigService, useValue: avatarServiceCongigSpy }
]
});

avatarService = TestBed.get(AvatarService);
httpMock = TestBed.get(HttpTestingController);
});
avatarService = TestBed.get(AvatarService);
httpMock = TestBed.get(HttpTestingController);
});

afterEach(() => {
httpMock.verify();
});
afterEach(() => {
httpMock.verify();
});

it("should be created", () => {
expect(avatarService).toBeTruthy();
});
it('should be created', () => {
expect(avatarService).toBeTruthy();
});

describe("fetchAvatar", () => {
it("should send get request and fetch avatar data from the given url", () => {
const avatarUrl = "dummy-avatar-url";
const expectedAvatarData = {
img: "url-for-avatar-img"
};
avatarService.fetchAvatar(avatarUrl).subscribe(avatarData => {
expect(avatarData).toEqual(expectedAvatarData);
describe('fetchAvatar', () => {
it('should send get request and fetch avatar data from the given url', () => {
const avatarUrl = 'dummy-avatar-url';
const expectedAvatarData = {
img: 'url-for-avatar-img'
};
avatarService.fetchAvatar(avatarUrl).subscribe(avatarData => {
expect(avatarData).toEqual(expectedAvatarData);
});

const req = httpMock.expectOne(
request => request.method === 'GET' && request.url === avatarUrl
);
req.flush(expectedAvatarData);
});

const req = httpMock.expectOne(
request => request.method === "GET" && request.url === avatarUrl
);
req.flush(expectedAvatarData);
});
});

describe("isSource", () => {
it("should return true when the given value is a valid avatar source", () => {
const isValidAvatar = avatarService.isSource(AvatarSource.GITHUB);
describe('isSource', () => {
it('should return true when the given value is a valid avatar source', () => {
const isValidAvatar = avatarService.isSource(AvatarSource.GITHUB);

expect(isValidAvatar).toBeTruthy();
});

it("should return false when the given value is not a valid avatar source", () => {
const isValidAvatar = avatarService.isSource("unknown-source");
expect(isValidAvatar).toBeTruthy();
});

expect(isValidAvatar).toBeFalsy();
});
});
it('should return false when the given value is not a valid avatar source', () => {
const isValidAvatar = avatarService.isSource('unknown-source');

describe("isTextAvatar", () => {
it("should return true when the given value is a text avatar", () => {
expect(avatarService.isTextAvatar(AvatarSource.INITIALS)).toBeTruthy();
expect(isValidAvatar).toBeFalsy();
});
});

it("should return false when the given value is not a text avatar", () => {
expect(avatarService.isTextAvatar(AvatarSource.GITHUB)).toBeFalsy();
describe('isTextAvatar', () => {
it('should return true when the given value is a text avatar', () => {
expect(avatarService.isTextAvatar(AvatarSource.INITIALS)).toBeTruthy();
});

it('should return false when the given value is not a text avatar', () => {
expect(avatarService.isTextAvatar(AvatarSource.GITHUB)).toBeFalsy();
});
});
});

describe("getRandomColor", () => {
it("should return transparent when the given value is undefined", () => {
const color = avatarService.getRandomColor(undefined);
describe('getRandomColor', () => {
it('should return transparent when the given value is undefined', () => {
const color = avatarService.getRandomColor(undefined);

expect(color).toBe("transparent");
});
expect(color).toBe('transparent');
});

it("should return a random color based on the ascii code of the given value is", () => {
const color = avatarService.getRandomColor("random name");
const cssColorRegex = /#([a-f]|[A-F]|[0-9]){3}(([a-f]|[A-F]|[0-9]){3})?\b/;
expect(color).toMatch(cssColorRegex);
});
it('should return a random color based on the ascii code of the given value is', () => {
const color = avatarService.getRandomColor('random name');
const cssColorRegex = /#([a-f]|[A-F]|[0-9]){3}(([a-f]|[A-F]|[0-9]){3})?\b/;
expect(color).toMatch(cssColorRegex);
});

it("should not return the same color for two different values", () => {
const color1 = avatarService.getRandomColor("name1");
const color2 = avatarService.getRandomColor("name2");
it('should not return the same color for two different values', () => {
const color1 = avatarService.getRandomColor('name1');
const color2 = avatarService.getRandomColor('name2');

expect(color1).not.toBe(color2);
expect(color1).not.toBe(color2);
});
});
});

describe("compareSources", () => {
it("should return a negative value when the first avatar type comes after the second one", () => {
expect(avatarService.copmareSources(AvatarSource.FACEBOOK, AvatarSource.GOOGLE)).toBeLessThan(0);
});
describe('compareSources', () => {
it('should return a negative value when the first avatar type comes after the second one', () => {
expect(
avatarService.copmareSources(
AvatarSource.FACEBOOK,
AvatarSource.GOOGLE
)
).toBeLessThan(0);
});

it("should return a positive value when the first avatar type comes before the second one", () => {
expect(avatarService.copmareSources(AvatarSource.INITIALS, AvatarSource.FACEBOOK)).toBeGreaterThan(0);
});
it('should return a positive value when the first avatar type comes before the second one', () => {
expect(
avatarService.copmareSources(
AvatarSource.INITIALS,
AvatarSource.FACEBOOK
)
).toBeGreaterThan(0);
});

it("should return a zero value when the two give values are equales", () => {
expect(avatarService.copmareSources(AvatarSource.GITHUB, AvatarSource.GITHUB)).toBe(0);
it('should return a zero value when the two give values are equales', () => {
expect(
avatarService.copmareSources(AvatarSource.GITHUB, AvatarSource.GITHUB)
).toBe(0);
});
});
});
});
Loading

0 comments on commit 118a7e6

Please sign in to comment.