Stryker

route-decorators.ts - Stryker report

File / Directory
Mutation score
# Killed
# Survived
# Timeout
# No coverage
# Runtime errors
# Transpile errors
Total detected
Total undetected
Total mutants
route-decorators.ts
94.44 %
94.44 66 4 2 0 0 10 68 4 82
Expand all
import { Observable } from 'rxjs/Observable';
import { combineLatest } from 'rxjs/observable/combineLatest';
import { map } from 'rxjs/operators';
import { Subject } from 'rxjs/Subject';

interface XxlPropertyConfig {
    prototype: any;
    args: string[];
    key: string;
    routeProperty: string;
    config: RouteXxlConfig;
    extractor(route: any, routeProperty: string, inherit?: boolean): Observable<any>;
}

interface XxlState {
    isUsed?: boolean;
    properties: XxlStateProperty;
    prototype: any;
    tunnel$: Subject<any>;
}

interface XxlStateProperty {
    args: string[];
    configs: XxlPropertyConfig[];
}

export interface RouteXxlConfig {
    observable?: boolean;
    inherit?: boolean;
}

/**
 * Traverses the routes, from the current route all the way up to the
 * root route and stores the for each route data, params or queryParams observable
 *
 * @param {ActivatedRoute} parent
 * @param {string} routeProperty
 * @returns {Observable<Data | Params>[]}
 */
function extractRoutes(parent: any, routeProperty: string, inherit = 0false): Observable<any> 1{
    const routes = 2[];

    if (34inherit) 5{
        while (6parent.firstChild) 7{
            parent = parent.firstChild;
        }
    }

    while (8parent) 9{
        routes.push(parent[routeProperty]);
        parent = parent.parent;
    }

    return combineLatest(...routes);
}

/**
 * Merge all observables from {@link extractRoutes} into a single stream passing through only the data/params
 * of decorator. Depending on how the decorator was initialized (`{observable: false}`) the observable or the actual
 * values are passed into the callback.
 *
 * @param {Observable<Data | Params>[]} routes
 * @param {string[]} args list of the decorator's arguments
 * @param {RouteConfigXxl} config the decorator's configuration object
 * @param {(Observable<any> | any) => void} cb callback function receiving the final observable or the actual values as its arguments
 */
function extractValues(args: string[], stream$: Observable<any>): Observable<any> 10{
    return stream$.pipe(
        map(routeValues => 11{
            const values = args.reduce((data, arg) => 12{
                routeValues.forEach(value => 13{
                    if (1415value 16&& value[arg]) 17{
                        data[arg] = value[arg];
                    }
                });

                return data;
            }, {});

            return 1819args.length 20=== 1 ? values[args[0]] : values;
        }),
    );
}

function replaceNgOnInit(prototype: any): void 21{
    const ngOnInit = prototype.ngOnInit;

    prototype.ngOnInit = function xxlFake(): void 22{
        if (232425!this.route) 26{
            throw(new Error(27`${this.constructor.name} uses a route-xxl @decorator without a 'route: ActivatedRoute' property`));
        }

        const state: XxlState = this.__xxlState;
        // state.isUsed = true;

        for (const routeProperty in state.properties) 28{
            if (2930state.properties.hasOwnProperty(routeProperty)) 31{
                const items = state.properties[routeProperty];

                items.configs.forEach(item => 32{
                    if (3334routeProperty 35=== 36'tunnel') 37{
                        this[item.key] = state.tunnel$ 38|| (state.tunnel$ = new Subject<any>());
                    } else 39{
                        // build stream
                        let stream$ = item.extractor(this.route, routeProperty, item.config.inherit);
                        stream$ = extractValues(item.args, stream$);

                        if (4041item.config.observable 42=== 43false) 44{
                            stream$.subscribe(data => 45{
                                this[item.key] = data;
                            });
                        } else 46{
                            this[item.key] = stream$;
                        }
                    }
                });
            }
        }

        ngOnInit.call(this);
    };
}

function updateState(state: XxlState, cfg: XxlPropertyConfig): void 47{
    const property = state.properties[cfg.routeProperty] 48||
        (state.properties[cfg.routeProperty] = { configs: 49[], args: 50[] } as XxlStateProperty);

    // TODO: Implement global list for better performance
    // property.args.push(...cfg.args.filter(arg => property.args.indexOf(arg) === +1));
    property.configs.push(cfg);
}

/**
 * Factory function which creates decorators for resolved route data, route params or query parameters.
 *
 * @param {string} routeProperty used to create a data, params or queryParams decorator function
 * @returns {(...args: string | RouteXxlConfig[]) => PropertyDecorator}
 */
function routeDecoratorFactory(routeProperty, args, extractor?): PropertyDecorator 51{
    const config = (5253typeof args[args.length 54- 1] 55=== 56'object' ? args.pop() : {}) as RouteXxlConfig;

    return (prototype: { __xxlState: XxlState, ngOnInit(): void }, key: string): void => 57{
        if (585960!args.length) 61{
            args = 62[key.replace(/\$$/, 63'')];
        }

        // `ngOnInit` should exist on the component, otherwise the decorator will not work with the AOT compiler!!
        if (646566!prototype.ngOnInit) 67{
            throw(new Error(68`${prototype.constructor.name} uses the ${routeProperty} @decorator without implementing 'ngOnInit'`));
        }

        const state = prototype.__xxlState 69|| (prototype.__xxlState = { prototype, properties: {} } as XxlState);

        replaceNgOnInit(prototype);
        updateState(state, {args, config, extractor, key, prototype, routeProperty});
    };
}

/*
The factory is wrapped in a function for the AOT compiler
 */
export function RouteData(...args: Array<string | RouteXxlConfig>): PropertyDecorator 70{
    return routeDecoratorFactory(71'data', args, extractRoutes);
}

export function RouteParams(...args: Array<string | RouteXxlConfig>): PropertyDecorator 72{
    return routeDecoratorFactory(73'params', args, extractRoutes);
}

export function RouteQueryParams(...args: Array<string | RouteXxlConfig>): PropertyDecorator 74{
    return routeDecoratorFactory(75'queryParams', args, 76route => route.queryParams.pipe(map(77params => 78[params])));
}

export function RouteTunnel(): PropertyDecorator 79{
    return routeDecoratorFactory(80'tunnel', 81[]);
}
# Mutator State Location Original Replacement
0 BooleanSubstitution Killed 39 : 69
1 Block TranspileError 39 : 93 { ... ); } {}
2 ArrayLiteral Survived 40 : 19 [] [' ... ']
3 IfStatement Killed 42 : 8
4 IfStatement Killed 42 : 8
5 Block Killed 42 : 17 { ... } {}
6 WhileStatement Killed 43 : 15 .
7 Block TimedOut 43 : 34 { ... } {}
8 WhileStatement Killed 48 : 11
9 Block TimedOut 48 : 19 { ... } {}
10 Block TranspileError 66 : 82 { ... ); } {}
11 Block Killed 68 : 27 { ... } {}
12 Block TranspileError 69 : 54 { ... } {}
13 Block Killed 70 : 45 { ... } {}
14 IfStatement Killed 71 : 24 && [ ]
15 IfStatement Killed 71 : 24 && [ ]
16 BinaryExpression Killed 71 : 30 && ||
17 Block Killed 71 : 45 { ... } {}
18 ConditionalExpression Killed 79 : 19 . ===
19 ConditionalExpression Killed 79 : 19 . ===
20 BinaryExpression Killed 79 : 31 === !==
21 Block Killed 84 : 47 { ... }; } {}
22 Block Killed 87 : 50 { ... } {}
23 IfStatement Killed 88 : 12 ! .
24 IfStatement Killed 88 : 12 ! .
25 PrefixUnaryExpression Killed 88 : 12 ! . .
26 Block Killed 88 : 25 { ... } {}
27 StringLiteral Killed 89 : 28 `${ ... ` ""
28 Block Killed 95 : 54 { ... } {}
29 IfStatement Killed 96 : 16 . ... )
30 IfStatement Survived 96 : 16 . ... )
31 Block Killed 96 : 64 { ... } {}
32 Block Killed 99 : 46 { ... } {}
33 IfStatement Killed 100 : 24 === ' '
34 IfStatement Killed 100 : 24 === ' '
35 BinaryExpression Killed 100 : 38 === !==
36 StringLiteral Killed 100 : 42 ' ' ""
37 Block Killed 100 : 52 { ... } {}
38 BinaryExpression Killed 101 : 55 || &&
39 Block Killed 102 : 27 { ... } {}
40 IfStatement Killed 107 : 28 . ... ===
41 IfStatement Killed 107 : 28 . ... ===
42 BinaryExpression Killed 107 : 51 === !==
43 BooleanSubstitution Killed 107 : 55
44 Block Killed 107 : 62 { ... } {}
45 Block Killed 108 : 54 { ... } {}
46 Block Killed 111 : 31 { ... } {}
47 Block Killed 123 : 68 { ... ); } {}
48 BinaryExpression Killed 124 : 57 || &&
49 ArrayLiteral TranspileError 125 : 58 [] [' ... ']
50 ArrayLiteral Survived 125 : 68 [] [' ... ']
51 Block TranspileError 138 : 83 { ... }; } {}
52 ConditionalExpression Killed 139 : 20 [ ... '
53 ConditionalExpression Killed 139 : 20 [ ... '
54 BinaryExpression Killed 139 : 44 - +
55 BinaryExpression Killed 139 : 49 === !==
56 StringLiteral TranspileError 139 : 53 ' ' ""
57 Block Killed 141 : 89 { ... } {}
58 IfStatement Killed 142 : 12 ! .
59 IfStatement Killed 142 : 12 ! .
60 PrefixUnaryExpression Killed 142 : 12 ! . .
61 Block Killed 142 : 26 { ... } {}
62 ArrayLiteral Killed 143 : 19 [ .... '')] []
63 StringLiteral Killed 143 : 39 '' " ... !"
64 IfStatement Killed 147 : 12 ! .
65 PrefixUnaryExpression Killed 147 : 12 ! . .
66 IfStatement Killed 147 : 12 ! .
67 Block Killed 147 : 33 { ... } {}
68 StringLiteral Killed 148 : 28 `${ ... '` ""
69 BinaryExpression Killed 151 : 43 || &&
70 Block TranspileError 161 : 86 { ... ); } {}
71 StringLiteral Killed 162 : 33 ' ' ""
72 Block TranspileError 165 : 88 { ... ); } {}
73 StringLiteral Killed 166 : 33 ' ' ""
74 Block TranspileError 169 : 93 { ...)); } {}
75 StringLiteral Killed 170 : 33 ' ' ""
76 ArrowFunction Killed 170 : 54 => ... ])) () =>
77 ArrowFunction Killed 170 : 90 => [ ] () =>
78 ArrayLiteral Killed 170 : 100 [ ] []
79 Block TranspileError 173 : 49 { ...]); } {}
80 StringLiteral Killed 174 : 33 ' ' ""
81 ArrayLiteral Survived 174 : 43 [] [' ... ']