Skip to content

Commit

Permalink
feat(metrics): move metrics to it's own package (#28)
Browse files Browse the repository at this point in the history
Move the calculation of the metrics to its own package.
This way we can reuse it in the console reporter and the
stryker dashboard.
  • Loading branch information
nicojs committed Apr 9, 2019
1 parent 3fd97a9 commit fc66b8b
Show file tree
Hide file tree
Showing 40 changed files with 788 additions and 604 deletions.
7 changes: 4 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
{
"type": "node",
"request": "launch",
"name": "Schema tests",
"name": "Mocha unit tests",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"-r",
"source-map-support/register",
"--timeout",
"999999",
"--colors",
"${workspaceFolder}/packages/mutation-testing-report-schema/dist/test/**/*.js"
"${workspaceFolder}/packages/mutation-testing-report-schema/dist/test/**/*.js",
"${workspaceFolder}/packages/mutation-testing-metrics/dist/test/**/*.js"
],
"internalConsoleOptions": "openOnSessionStart"
},
Expand Down Expand Up @@ -45,7 +46,7 @@
{
"type": "node",
"request": "launch",
"name": "Unit tests tests (Mutation testing elements)",
"name": "Karma unit tests (Mutation testing elements)",
"program": "${workspaceFolder}/node_modules/karma/bin/karma",
"args": [
"start"
Expand Down
1 change: 1 addition & 0 deletions packages/mutation-testing-elements/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@types/sinon-chai": "^3.2.2",
"lodash.groupby": "^4.6.0",
"mutation-testing-report-schema": "^1.0.5",
"mutation-testing-metrics": "^1.0.0",
"rxjs": "^6.4.0",
"sinon-chai": "^3.3.0"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { LitElement, html, property, customElement, css, PropertyValues } from 'lit-element';
import { MutationTestResult } from 'mutation-testing-report-schema';
import { normalizeFileNames } from '../lib/helpers';
import { MetricsResult, calculateMetrics } from 'mutation-testing-metrics';
import { bootstrap } from '../style';
import { ResultModel } from '../model/ResultModel';
import { toDirectoryModel } from '../model';
import { locationChange$ } from '../lib/router';
import { Subscription } from 'rxjs';

Expand All @@ -14,7 +12,7 @@ export class MutationTestReportAppComponent extends LitElement {
public report: MutationTestResult | undefined;

@property({ attribute: false })
public rootModel: ResultModel | undefined;
public rootModel: MetricsResult | undefined;

@property()
public src: string | undefined;
Expand All @@ -23,7 +21,7 @@ export class MutationTestReportAppComponent extends LitElement {
public errorMessage: string | undefined;

@property({ attribute: false })
public context: ResultModel | undefined;
public context: MetricsResult | undefined;

@property()
public path: ReadonlyArray<string> = [];
Expand Down Expand Up @@ -68,12 +66,15 @@ export class MutationTestReportAppComponent extends LitElement {
}

private updateModel(report: MutationTestResult) {
this.rootModel = toDirectoryModel(normalizeFileNames(report.files));
this.rootModel = calculateMetrics(report.files);
}

private updateContext() {
if (this.rootModel) {
this.context = this.rootModel.find(this.path.join('/'));
// Find the current selected file/directory based on the path
this.context = this.path.reduce<MetricsResult | undefined>(
(model, currentPathPart) => model && model.childResults.find(child => child.name === currentPathPart),
this.rootModel);
}
}

Expand Down Expand Up @@ -123,7 +124,7 @@ export class MutationTestReportAppComponent extends LitElement {
return undefined;
function renderPostfix() {
if (self.titlePostfix) {
return html`<small class="text-muted"> - ${self.titlePostfix}</small>`;
return html`<small class="text-muted"> - ${self.titlePostfix}</small>`;
} else {
return undefined;
}
Expand Down Expand Up @@ -170,8 +171,8 @@ export class MutationTestReportAppComponent extends LitElement {
}

private renderFileReport() {
if (this.context && this.report && this.context.representsFile) {
return html`<mutation-test-report-file .model="${this.context}"></mutation-test-report-file>`;
if (this.context && this.report && this.context.file) {
return html`<mutation-test-report-file .model="${this.context.file}"></mutation-test-report-file>`;
} else {
return undefined;
}
Expand All @@ -182,7 +183,8 @@ export class MutationTestReportAppComponent extends LitElement {
return html`
<div class='row'>
<div class='totals col-sm-11'>
<mutation-test-report-totals .thresholds="${this.report.thresholds}" .model="${this.context}"></mutation-test-report-totals>
<mutation-test-report-totals .currentPath="${this.path}" .thresholds="${this.report.thresholds}" .model="${this.context}">
</mutation-test-report-totals>
</div>
</div>
`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LitElement, html, property, customElement } from 'lit-element';
import { bootstrap } from '../style';
import { ROOT_NAME } from '../lib/helpers';
const ROOT_NAME = 'All files';

@customElement('mutation-test-report-breadcrumb')
export class MutationTestReportBreadcrumbComponent extends LitElement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import typescript from 'highlight.js/lib/languages/typescript';
import { MutationTestReportMutantComponent } from '../mutation-test-report-mutant';
import { MutantFilter } from '../mutation-test-report-file-legend';
import { bootstrap, highlightJS } from '../../style';
import { FileResultModel } from '../../model';
import { renderCode } from '../../lib/helpers';
import { FileResult } from 'mutation-testing-report-schema';

hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('typescript', typescript);
Expand All @@ -20,7 +20,7 @@ hljs.registerLanguage('scala', scala);
export class MutationTestReportFileComponent extends LitElement {

@property()
public model!: FileResultModel;
public model!: FileResult;

public static styles = [
highlightJS,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { LitElement, html, property, customElement, css } from 'lit-element';
import { bootstrap } from '../style';
import { FileResultModel, DirectoryResultModel } from '../model';
import { Thresholds } from 'mutation-testing-report-schema';
import * as svg from './svg';
import { pathJoin } from '../lib/helpers';
import { ResultModel } from '../model/ResultModel';
import { MetricsResult } from 'mutation-testing-metrics';

@customElement('mutation-test-report-totals')
export class MutationTestReportTotalsComponent extends LitElement {

@property()
public model: FileResultModel | DirectoryResultModel | undefined;
public model: MetricsResult | undefined;

@property()
public thresholds: Thresholds | undefined;

@property()
public currentPath: string[] = [];

public static styles = [bootstrap,
css`
.table a {
Expand Down Expand Up @@ -118,36 +120,37 @@ export class MutationTestReportTotalsComponent extends LitElement {
</thead>`;
}

private renderTableBody(model: FileResultModel | DirectoryResultModel) {
private renderTableBody(model: MetricsResult) {
const renderChildren = () => {
if (!model.representsFile) {
if (model.file) {
return undefined;
} else {
return model.childResults.map(childResult => {
let fullName: string = childResult.name;
while (!childResult.representsFile && childResult.childResults.length === 1) {
while (!childResult.file && childResult.childResults.length === 1) {
childResult = childResult.childResults[0];
fullName = pathJoin(fullName, childResult.name);
}
return this.renderRow(fullName, childResult, true);
return this.renderRow(fullName, childResult, pathJoin(...this.currentPath, fullName));
});
} else {
return undefined;
}
};
return html`
<tbody>
${this.renderRow(model.name, model, false)}
${this.renderRow(model.name, model, undefined)}
${renderChildren()}
</tbody>`;
}

private renderRow(name: string, row: ResultModel, shouldLink: boolean) {
const mutationScoreRounded = row.totals.mutationScore.toFixed(2);
const coloringClass = this.determineColoringClass(row.totals.mutationScore);
private renderRow(name: string, row: MetricsResult, path: string | undefined) {
const mutationScoreRounded = row.metrics.mutationScore.toFixed(2);
const coloringClass = this.determineColoringClass(row.metrics.mutationScore);
const progressBarStyle = `width: ${mutationScoreRounded}%`;
return html`
<tr title="${row.name}">
<td style="width: 17px;" class="icon no-border-right">${row.representsFile ? svg.file : svg.directory}</td>
<td width="" class="no-border-left">${shouldLink ? html`<a href="${this.link(row.path)}">${name}</a>` : html`<span>${row.name}</span>`}</td>
<td style="width: 17px;" class="icon no-border-right">${row.file ? svg.file : svg.directory}</td>
<td width="" class="no-border-left">${typeof path === 'string' ? html`<a href="${this.link(path)}">${name}</a>` :
html`<span>${row.name}</span>`}</td>
<td class="no-border-right vertical-middle">
<div class="progress">
<div class="progress-bar bg-${coloringClass}" role="progressbar" aria-valuenow="${mutationScoreRounded}"
Expand All @@ -157,15 +160,15 @@ export class MutationTestReportTotalsComponent extends LitElement {
</div>
</td>
<th style="width: 50px;" class="no-border-left text-center text-${coloringClass}">${mutationScoreRounded}</th>
<td class="text-center">${row.totals.killed}</td>
<td class="text-center">${row.totals.survived}</td>
<td class="text-center">${row.totals.timeout}</td>
<td class="text-center">${row.totals.noCoverage}</td>
<td class="text-center">${row.totals.runtimeErrors}</td>
<td class="text-center">${row.totals.compileErrors}</td>
<th class="text-center">${row.totals.totalDetected}</th>
<th class="text-center">${row.totals.totalUndetected}</th>
<th class="text-center">${row.totals.totalMutants}</th>
<td class="text-center">${row.metrics.killed}</td>
<td class="text-center">${row.metrics.survived}</td>
<td class="text-center">${row.metrics.timeout}</td>
<td class="text-center">${row.metrics.noCoverage}</td>
<td class="text-center">${row.metrics.runtimeErrors}</td>
<td class="text-center">${row.metrics.compileErrors}</td>
<th class="text-center">${row.metrics.totalDetected}</th>
<th class="text-center">${row.metrics.totalUndetected}</th>
<th class="text-center">${row.metrics.totalMutants}</th>
</tr>`;
}

Expand Down
51 changes: 3 additions & 48 deletions packages/mutation-testing-elements/src/lib/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,18 @@
import { FileResultDictionary, MutantResult, Position } from 'mutation-testing-report-schema';
import { FileResultModel } from '../model';
import { MutantResult, Position, FileResult } from 'mutation-testing-report-schema';
import { BackgroundColorCalculator } from './BackgroundColorCalculator';
import { escapeHtml } from './htmlHelpers';

export const ROOT_NAME = 'All files';
const SEPARATOR = '/';

export function flatMap<T, R>(source: T[], fn: (input: T) => R[]) {
const result: R[] = [];
source.map(fn).forEach(items => result.push(...items));
return result;
}

export function pathJoin(...parts: string[]) {
return parts.reduce((prev, current) => prev.length ? current ? `${prev}/${current}` : prev : current, '');
}

export function normalizeFileNames(input: FileResultDictionary): FileResultDictionary {
const fileNames = Object.keys(input);
const commonBasePath = determineCommonBasePath(fileNames);
const output: FileResultDictionary = {};
fileNames.forEach(fileName => {
output[normalize(fileName.substr(commonBasePath.length))] = input[fileName];
});
return output;
}

function normalize(fileName: string) {
return fileName.split(/\/|\\/)
.filter(pathPart => pathPart)
.join('/');
}

export function determineCommonBasePath(fileNames: ReadonlyArray<string>) {
const directories = fileNames.map(fileName => fileName.split(/\/|\\/).slice(0, -1));
if (fileNames.length) {
return directories.reduce(filterDirectories).join(SEPARATOR);
} else {
return '';
}

function filterDirectories(previousDirectories: string[], currentDirectories: string[]) {
for (let i = 0; i < previousDirectories.length; i++) {
if (previousDirectories[i] !== currentDirectories[i]) {
return previousDirectories.splice(0, i);
}
}

return previousDirectories;
}
}

/**
* Walks over the code in this.model.source and adds the
* Walks over the code in model.source and adds the
* `<mutation-test-report-mutant>` elements.
* It also adds the background color using
* `<span class="bg-danger-light">` and friends.
*/
export function renderCode(model: FileResultModel): string {
export function renderCode(model: FileResult): string {
const backgroundState = new BackgroundColorCalculator();
const startedMutants: MutantResult[] = [];
const walker = (char: string, pos: Position): string => {
Expand Down
2 changes: 1 addition & 1 deletion packages/mutation-testing-elements/src/lib/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ export const locationChange$ = merge(of(1),
fromEvent<HashChangeEvent>(window, 'hashchange').pipe(
tap(event => event.preventDefault())
)).pipe(
map(_ => window.location.hash.substr(1).split('/'))
map(_ => window.location.hash.substr(1).split('/').filter(part => part !== ''))
);

This file was deleted.

21 changes: 0 additions & 21 deletions packages/mutation-testing-elements/src/model/FileResultModel.ts

This file was deleted.

12 changes: 0 additions & 12 deletions packages/mutation-testing-elements/src/model/ResultModel.ts

This file was deleted.

0 comments on commit fc66b8b

Please sign in to comment.