test(aio): fill in missing API page unit tests
This commit is contained in:
parent
09574fc285
commit
0e6eb6d719
|
@ -47,6 +47,7 @@ describe('AppComponent', () => {
|
|||
});
|
||||
|
||||
describe('isHamburgerVisible', () => {
|
||||
console.log('PENDING: AppComponent isHamburgerVisible');
|
||||
});
|
||||
|
||||
describe('onResize', () => {
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
import { ApiListComponent } from './api-list.component';
|
||||
import { ApiItem, ApiSection, ApiService } from './api.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
|
||||
describe('ApiListComponent', () => {
|
||||
let component: ApiListComponent;
|
||||
let fixture: ComponentFixture<ApiListComponent>;
|
||||
let sections: ApiSection[];
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ApiListComponent ],
|
||||
providers: [
|
||||
{ provide: ApiService, useClass: TestApiService },
|
||||
{ provide: LocationService, useClass: TestLocationService }
|
||||
]
|
||||
});
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ApiListComponent);
|
||||
component = fixture.componentInstance;
|
||||
sections = getApiSections();
|
||||
});
|
||||
|
||||
it('should be creatable', () => {
|
||||
expect(component).toBeDefined();
|
||||
});
|
||||
|
||||
/**
|
||||
* Expectation Utility: Assert that filteredSections has the expected result for this test
|
||||
* @param itemTest - return true if the item passes the match test
|
||||
*
|
||||
* Subscibes to `filteredSections` and performs expectation within subscription callback.
|
||||
*/
|
||||
function expectFilteredResult(label: string, itemTest: (item: ApiItem) => boolean) {
|
||||
component.filteredSections.subscribe(filtered => {
|
||||
let badItem: ApiItem;
|
||||
expect(filtered.length).toBeGreaterThan(0, 'expected something');
|
||||
expect(filtered.every(section => section.items.every(
|
||||
item => {
|
||||
const ok = item.show === itemTest(item);
|
||||
if (!ok) { badItem = item; }
|
||||
return ok;
|
||||
}
|
||||
))).toBe(true, `${label} fail: ${JSON.stringify(badItem, null, 2)}`);
|
||||
});
|
||||
}
|
||||
|
||||
describe('#filteredSections', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should return all complete sections when no criteria', () => {
|
||||
let filtered: ApiSection[];
|
||||
component.filteredSections.subscribe(f => filtered = f);
|
||||
expect(filtered).toEqual(sections);
|
||||
});
|
||||
|
||||
it('item.show should be true for all queried items', () => {
|
||||
component.setQuery('class');
|
||||
expectFilteredResult('query: class', item => /class/.test(item.name));
|
||||
});
|
||||
|
||||
it('item.show should be true for every item in section when query matches section name', () => {
|
||||
component.setQuery('core');
|
||||
component.filteredSections.subscribe(filtered => {
|
||||
expect(filtered.length).toBe(1, 'only one section');
|
||||
expect(filtered[0].name).toBe('core');
|
||||
expect(filtered[0].items.every(item => item.show)).toBe(true, 'all core items shown');
|
||||
});
|
||||
});
|
||||
|
||||
it('item.show should be true for items with selected status', () => {
|
||||
component.setStatus({name: 'stable', title: 'Stable'});
|
||||
expectFilteredResult('status: stable', item => item.stability === 'stable');
|
||||
});
|
||||
|
||||
it('item.show should be true for items with "security-risk" status when selected', () => {
|
||||
component.setStatus({name: 'security-risk', title: 'Security Risk'});
|
||||
expectFilteredResult('status: security-risk', item => item.securityRisk);
|
||||
});
|
||||
|
||||
it('item.show should be true for items of selected type', () => {
|
||||
component.setType({name: 'class', title: 'Class'});
|
||||
expectFilteredResult('type: class', item => item.docType === 'class');
|
||||
});
|
||||
|
||||
it('should have no sections and no items when no match', () => {
|
||||
component.setQuery('fizbuzz');
|
||||
component.filteredSections.subscribe(filtered => {
|
||||
expect(filtered.length).toBe(0, 'expected no sections');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('initial critera from location', () => {
|
||||
let locationService: TestLocationService;
|
||||
|
||||
beforeEach(() => {
|
||||
locationService = <any> fixture.componentRef.injector.get(LocationService);
|
||||
});
|
||||
|
||||
function expectOneItem(name: string, section: string, type: string, stability: string) {
|
||||
fixture.detectChanges();
|
||||
|
||||
component.filteredSections.subscribe(filtered => {
|
||||
expect(filtered.length).toBe(1, 'sections');
|
||||
expect(filtered[0].name).toBe(section, 'section name');
|
||||
const items = filtered[0].items.filter(item => item.show);
|
||||
expect(items.length).toBe(1, 'items');
|
||||
|
||||
const item = items[0];
|
||||
const badItem = 'Wrong item: ' + JSON.stringify(item, null, 2);
|
||||
|
||||
expect(item.docType).toBe(type, badItem);
|
||||
expect(item.stability).toBe(stability, badItem);
|
||||
expect(item.name).toBe(name, badItem);
|
||||
});
|
||||
}
|
||||
|
||||
it('should filter as expected for ?query', () => {
|
||||
locationService.query = {query: '_3'};
|
||||
expectOneItem('class_3', 'core', 'class', 'experimental');
|
||||
});
|
||||
|
||||
it('should filter as expected for ?status', () => {
|
||||
locationService.query = {status: 'deprecated'};
|
||||
expectOneItem('function_1', 'core', 'function', 'deprecated');
|
||||
});
|
||||
|
||||
it('should filter as expected when status is security-risk', () => {
|
||||
locationService.query = {status: 'security-risk'};
|
||||
fixture.detectChanges();
|
||||
expectFilteredResult('security-risk', item => item.securityRisk);
|
||||
});
|
||||
|
||||
it('should filter as expected for ?type', () => {
|
||||
locationService.query = {type: 'pipe'};
|
||||
expectOneItem('pipe_1', 'common', 'pipe', 'stable');
|
||||
});
|
||||
|
||||
it('should filter as expected for ?query&status&type', () => {
|
||||
locationService.query = {
|
||||
query: 's_1',
|
||||
status: 'experimental',
|
||||
type: 'class'
|
||||
};
|
||||
fixture.detectChanges();
|
||||
expectOneItem('class_1', 'common', 'class', 'experimental');
|
||||
});
|
||||
|
||||
it('should ignore case for ?query&status&type', () => {
|
||||
locationService.query = {
|
||||
query: 'S_1',
|
||||
status: 'ExperiMental',
|
||||
type: 'CLASS'
|
||||
};
|
||||
fixture.detectChanges();
|
||||
expectOneItem('class_1', 'common', 'class', 'experimental');
|
||||
});
|
||||
});
|
||||
|
||||
describe('location path after criteria change', () => {
|
||||
let locationService: TestLocationService;
|
||||
|
||||
beforeEach(() => {
|
||||
locationService = <any> fixture.componentRef.injector.get(LocationService);
|
||||
});
|
||||
|
||||
it('should have query', () => {
|
||||
component.setQuery('foo');
|
||||
|
||||
// `setSearch` 2nd param is a query/search params object
|
||||
const search = locationService.setSearch.calls.mostRecent().args[1];
|
||||
expect(search.query).toBe('foo');
|
||||
});
|
||||
|
||||
it('should keep last of multiple query settings (in lowercase)', () => {
|
||||
component.setQuery('foo');
|
||||
component.setQuery('fooBar');
|
||||
|
||||
const search = locationService.setSearch.calls.mostRecent().args[1];
|
||||
expect(search.query).toBe('foobar');
|
||||
});
|
||||
|
||||
it('should have query, status, and type', () => {
|
||||
component.setQuery('foo');
|
||||
component.setStatus({name: 'stable', title: 'Stable'});
|
||||
component.setType({name: 'class', title: 'Class'});
|
||||
|
||||
const search = locationService.setSearch.calls.mostRecent().args[1];
|
||||
expect(search.query).toBe('foo');
|
||||
expect(search.status).toBe('stable');
|
||||
expect(search.type).toBe('class');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
////// Helpers ////////
|
||||
|
||||
class TestLocationService {
|
||||
query: {[index: string]: string } = {};
|
||||
setSearch = jasmine.createSpy('setSearch');
|
||||
search() { return this.query; }
|
||||
}
|
||||
|
||||
class TestApiService {
|
||||
sectionsSubject = new BehaviorSubject(getApiSections());
|
||||
sections = this.sectionsSubject.asObservable();
|
||||
}
|
||||
|
||||
// tslint:disable:quotemark
|
||||
const apiSections: ApiSection[] = [
|
||||
{
|
||||
"name": "common",
|
||||
"title": "common",
|
||||
"items": [
|
||||
{
|
||||
"name": "class_1",
|
||||
"title": "Class 1",
|
||||
"path": "api/common/class_1",
|
||||
"docType": "class",
|
||||
"stability": "experimental",
|
||||
"securityRisk": false
|
||||
},
|
||||
{
|
||||
"name": "class_2",
|
||||
"title": "Class 2",
|
||||
"path": "api/common/class_2",
|
||||
"docType": "class",
|
||||
"stability": "stable",
|
||||
"securityRisk": false
|
||||
},
|
||||
{
|
||||
"name": "directive_1",
|
||||
"title": "Directive 1",
|
||||
"path": "api/common/directive_1",
|
||||
"docType": "directive",
|
||||
"stability": "stable",
|
||||
"securityRisk": true
|
||||
},
|
||||
{
|
||||
"name": "pipe_1",
|
||||
"title": "Pipe 1",
|
||||
"path": "api/common/pipe_1",
|
||||
"docType": "pipe",
|
||||
"stability": "stable",
|
||||
"securityRisk": true
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "core",
|
||||
"title": "core",
|
||||
"items": [
|
||||
{
|
||||
"name": "class_3",
|
||||
"title": "Class 3",
|
||||
"path": "api/core/class_3",
|
||||
"docType": "class",
|
||||
"stability": "experimental",
|
||||
"securityRisk": false
|
||||
},
|
||||
{
|
||||
"name": "function_1",
|
||||
"title": "Function 1",
|
||||
"path": "api/core/function 1",
|
||||
"docType": "function",
|
||||
"stability": "deprecated",
|
||||
"securityRisk": true
|
||||
},
|
||||
{
|
||||
"name": "const_1",
|
||||
"title": "Const 1",
|
||||
"path": "api/core/const_1",
|
||||
"docType": "const",
|
||||
"stability": "stable",
|
||||
"securityRisk": false
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
function getApiSections() { return apiSections; }
|
|
@ -0,0 +1,125 @@
|
|||
import { ReflectiveInjector } from '@angular/core';
|
||||
import { Http, ConnectionBackend, RequestOptions, BaseRequestOptions, Response, ResponseOptions } from '@angular/http';
|
||||
import { MockBackend, MockConnection } from '@angular/http/testing';
|
||||
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
describe('ApiService', () => {
|
||||
|
||||
let injector: ReflectiveInjector;
|
||||
let service: ApiService;
|
||||
let backend: MockBackend;
|
||||
|
||||
function createResponse(body: any) {
|
||||
return new Response(new ResponseOptions({ body: JSON.stringify(body) }));
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
injector = ReflectiveInjector.resolveAndCreate([
|
||||
ApiService,
|
||||
{ provide: ConnectionBackend, useClass: MockBackend },
|
||||
{ provide: RequestOptions, useClass: BaseRequestOptions },
|
||||
Http,
|
||||
{ provide: Logger, useClass: TestLogger }
|
||||
]);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
backend = injector.get(ConnectionBackend);
|
||||
service = injector.get(ApiService);
|
||||
});
|
||||
|
||||
it('should be creatable', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not immediately connect to the server', () => {
|
||||
expect(backend.connectionsArray.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('subscribers should be completed/unsubscribed when service destroyed', () => {
|
||||
let completed = false;
|
||||
|
||||
service.sections.subscribe(
|
||||
null,
|
||||
null,
|
||||
() => completed = true
|
||||
);
|
||||
|
||||
service.ngOnDestroy();
|
||||
expect(completed).toBe(true);
|
||||
});
|
||||
|
||||
describe('#sections', () => {
|
||||
it('first subscriber should fetch sections', () => {
|
||||
const data = [{name: 'a'}, {name: 'b'}];
|
||||
|
||||
service.sections.subscribe(sections => {
|
||||
expect(sections).toEqual(data);
|
||||
});
|
||||
|
||||
backend.connectionsArray[0].mockRespond(createResponse(data));
|
||||
});
|
||||
|
||||
it('second subscriber should get previous sections and NOT trigger refetch', () => {
|
||||
const data = [{name: 'a'}, {name: 'b'}];
|
||||
let subscriptions = 0;
|
||||
|
||||
service.sections.subscribe(sections => {
|
||||
subscriptions++;
|
||||
expect(sections).toEqual(data);
|
||||
});
|
||||
|
||||
service.sections.subscribe(sections => {
|
||||
subscriptions++;
|
||||
expect(sections).toEqual(data);
|
||||
});
|
||||
|
||||
backend.connectionsArray[0].mockRespond(createResponse(data));
|
||||
|
||||
expect(backend.connectionsArray.length).toBe(1, 'server connections');
|
||||
expect(subscriptions).toBe(2, 'subscriptions');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#fetchSections', () => {
|
||||
|
||||
it('should connect to the server w/ expected URL', () => {
|
||||
service.fetchSections();
|
||||
expect(backend.connectionsArray.length).toEqual(1);
|
||||
expect(backend.connectionsArray[0].request.url).toEqual('content/docs/api/api-list.json');
|
||||
});
|
||||
|
||||
it('should refresh the #sections observable w/ new content on second call', () => {
|
||||
|
||||
let call = 0;
|
||||
let connection: MockConnection;
|
||||
backend.connections.subscribe(c => connection = c);
|
||||
|
||||
let data = [{name: 'a'}, {name: 'b'}];
|
||||
|
||||
service.sections.subscribe(sections => {
|
||||
// called twice during this test
|
||||
// (1) during subscribe
|
||||
// (2) after refresh
|
||||
expect(sections).toEqual(data, 'call ' + call++);
|
||||
});
|
||||
connection.mockRespond(createResponse(data));
|
||||
|
||||
// refresh/refetch
|
||||
data = [{name: 'c'}];
|
||||
service.fetchSections();
|
||||
connection.mockRespond(createResponse(data));
|
||||
|
||||
expect(call).toBe(2, 'should be called twice');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestLogger {
|
||||
log = jasmine.createSpy('log');
|
||||
error = jasmine.createSpy('error');
|
||||
}
|
Loading…
Reference in New Issue