Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/router handler #14

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Basic input/output example:

![Basic](./assets/component-example.png)

With angular router use (built in):
![Angular router](./assets/component-example-with-angular-router.png)

With custom event bus service dependency handler:

![With custom event service dependency handler](./assets/component-example-2.png)
Expand Down
Binary file added assets/component-example-with-angular-router.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { HomePageComponent } from './home-page.component';
import { Router, ActivatedRoute } from '@angular/router';
import { ActivatedRoute, ParamMap, Router, RouterEvent } from '@angular/router';
import { Observable, ReplaySubject } from 'rxjs';

describe('HomePageComponent', () => {
let component: HomePageComponent;
let fixture: ComponentFixture<HomePageComponent>;
let fakeRouter: jasmine.SpyObj<Router>;
let routerEventsSubject: ReplaySubject<RouterEvent>;
let fakeRoute: jasmine.SpyObj<ActivatedRoute>;
let routeParams: { [prop: string]: string };
let routeParamMap: jasmine.SpyObj<ParamMap>;
let routeParamsSubject: ReplaySubject<ParamMap>;
let queryRouteParams: { [prop: string]: string };
let routeQueryParamMap: jasmine.SpyObj<ParamMap>;
let queryRouteParamsSubject: ReplaySubject<ParamMap>;
let fakeWindow: jasmine.SpyObj<Window>;

beforeEach(waitForAsync(() => {
fakeRouter = jasmine.createSpyObj<Router>('Router', ['navigate']);
routerEventsSubject = new ReplaySubject<RouterEvent>(1);
fakeRoute = {} as jasmine.SpyObj<ActivatedRoute>;
routeParams = {};
routeParamMap = jasmine.createSpyObj<ParamMap>('ParamMap', ['get', 'has']);
routeParamMap.get.and.callFake((k) => routeParams[k]);
routeParamMap.has.and.callFake((k) => !!routeParams[k]);
routeParamsSubject = (fakeRoute as { paramMap: Observable<ParamMap> }).paramMap = new ReplaySubject<ParamMap>(1);
queryRouteParams = {};
routeQueryParamMap = jasmine.createSpyObj<ParamMap>('ParamMap', ['get', 'has']);
routeQueryParamMap.get.and.callFake((k) => queryRouteParams[k]);
routeQueryParamMap.has.and.callFake((k) => !!queryRouteParams[k]);
queryRouteParamsSubject = (fakeRoute as { queryParamMap: Observable<ParamMap> }).queryParamMap = new ReplaySubject<ParamMap>(1);
fakeWindow = {} as jasmine.SpyObj<Window>;

TestBed.configureTestingModule({
Expand Down
21 changes: 20 additions & 1 deletion spec/fixtures/components/home-page/home-page.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, Inject, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Router, ActivatedRoute, NavigationStart } from '@angular/router';
import { take, filter } from 'rxjs/operators';

@Component({
selector: 'app-name',
Expand All @@ -14,10 +15,28 @@ export class HomePageComponent implements OnInit {
) { }

ngOnInit(): void {
const value = this.route.snapshot.queryParams['value'];
console.info(value);

this.route.paramMap.subscribe(params => {
if (params.has('type') && params.get('type') === 'user') {
this.router.navigate(['home', 'user']);
}
});

this.route.queryParamMap.subscribe(params => {
if (params.has('type') && params.get('type') === 'user') {
this.router.navigate(['home', 'user']);
}
});

this.router.events
.pipe(
take(1),
filter(ev => ev instanceof NavigationStart)
)
.subscribe(()=>{
console.info('nav start event')
});
}
}
103 changes: 103 additions & 0 deletions src/dependency-handlers/angular.router.handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@

import { DependencyHandler } from '../model';
import defaultDependencyHandler from './default.handler';

export default {
run(result, dep, options) {
defaultDependencyHandler.run(result, dep, options);

// extra subject to support router events if property is used
if (dep.type === 'Router' && options.sourceCode.includes(`${dep.name}.events`)) {
result.imports.push({
path: 'rxjs',
names: ['ReplaySubject']
});
result.imports.push({
path: '@angular/router',
names: ['RouterEvent']
});

result.declarations.push({
name: 'routerEventsSubject',
type: 'ReplaySubject<RouterEvent>'
});

result.initializers.push({
name: 'routerEventsSubject',
value: 'new ReplaySubject<RouterEvent>(1)'
});
}

if (dep.type === 'ActivatedRoute') {
const usesParamMap = options.sourceCode.includes(`${dep.name}.paramMap`);
const usesQueryParamMap = options.sourceCode.includes(`${dep.name}.queryParamMap`);

// param map mock
if (usesParamMap || usesQueryParamMap) {
result.imports.push({
path: '@angular/router',
names: ['ParamMap']
});

result.imports.push({
path: 'rxjs',
names: ['Observable']
});
}
const variants: [apiName: string, varName: string, mapName: string][] = [];
if (usesParamMap) {
variants.push(['paramMap', 'routeParams', 'routeParamMap']);
}
if (usesQueryParamMap) {
variants.push(['queryParamMap', 'queryRouteParams', 'routeQueryParamMap']);
}

variants.forEach(([apiName, varName, mapName]) => {
result.declarations.push({
name: varName,
type: '{ [prop: string]: string }'
});

result.initializers.push({
name: varName,
value: '{}'
});

result.declarations.push({
name: `${mapName}`,
type: 'jasmine.SpyObj<ParamMap>'
});

result.initializers.push({
name: `${mapName}`,
value: `jasmine.createSpyObj<ParamMap>(${options.quoteSymbol}ParamMap${options.quoteSymbol}, [${options.quoteSymbol}get${options.quoteSymbol}, ${options.quoteSymbol}has${options.quoteSymbol}])`
});

result.initializers.push({
value: `${mapName}.get.and.callFake((k) => ${varName}[k])`
});

result.initializers.push({
value: `${mapName}.has.and.callFake((k) => !!${varName}[k])`
});

result.declarations.push({
name: `${varName}Subject`,
type: 'ReplaySubject<ParamMap>'
});

result.initializers.push({
// cast below makes sure that typescript is checking asignment
// this is basically a workaround for readonly properties
name: `${varName}Subject`,
value: `(${options.variableName} as { ${apiName}: Observable<ParamMap> }).${apiName} = new ReplaySubject<ParamMap>(1)`
});
});

}
},

test(dep) {
return dep.type === 'Router' || dep.type === 'ActivatedRoute';
}
} as DependencyHandler;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ParsedClassDependency, ClassOptions, DependencyHandler, DependencyHandlerOptions } from './model';
import { getUsedMethods } from './helpers';
import { ParsedClassDependency, ClassOptions, DependencyHandler, DependencyHandlerOptions } from '../model';
import { getUsedMethods } from '../helpers';

export default {
run(result: ClassOptions, dep: ParsedClassDependency, options: DependencyHandlerOptions) {
Expand Down
11 changes: 11 additions & 0 deletions src/dependency-handlers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { DependencyHandler } from '../model';
import _defaultDependencyHandler from './default.handler';
import _angularRouterHandler from './angular.router.handler';

export const defaultDependencyHandler: DependencyHandler = _defaultDependencyHandler;
export const angularRouterHandler: DependencyHandler = _angularRouterHandler;

export const dependencyHandlers: DependencyHandler[] = [
angularRouterHandler,
defaultDependencyHandler,
];
6 changes: 5 additions & 1 deletion src/generate-unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,21 @@ function prepareImports(imports: ParsedImport[], quoteSymbol: string): ParsedImp
let index = 0;
while (index < imports.length) {
const value = imports[index];
if (!value.path.match(/['"']/)) {
if (!value.path.match(/['"]/)) {
value.path = quoteSymbol + value.path + quoteSymbol;
}
result.push(value);
index++;

for (let i = imports.length - 1; i >= index; i--) {
const target = imports[i];
if (!target.path.match(/['"]/)) {
target.path = quoteSymbol + target.path + quoteSymbol;
}

if (target.path === value.path) {
value.names = uniq(value.names.concat(target.names));
value.names.sort();
imports.splice(i, 1);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { readFileSync, writeFileSync, readdirSync } from 'fs';
import * as ts from 'typescript';
import { parseSourceFile } from './parse-source-file';
import { generateUnitTest } from './generate-unit-test';
import defaultDependencyHandler from './default-dependency-handler';
import { DependencyHandler } from './model';
import { dependencyHandlers } from './dependency-handlers';

export function run(params: string[]) {
if (!params.length) {
Expand All @@ -26,7 +26,7 @@ export function run(params: string[]) {
});
params = params.slice(2);
}
handlers.push(defaultDependencyHandler);
handlers.push(...dependencyHandlers);

const path = params[0];

Expand Down
6 changes: 2 additions & 4 deletions src/public-api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import dependencyHandler from './default-dependency-handler';

export * from './dependency-handlers';
export * from './helpers';
export * from './model';
export { run } from './main';
export const defaultDependencyHandler = dependencyHandler;
export { run } from './main';