Skip to content

Commit

Permalink
Add 95% and 99% response time metrics to the report overview (#401)
Browse files Browse the repository at this point in the history
  • Loading branch information
ludeknovy authored Jun 1, 2024
1 parent 745a25c commit 75ecc3a
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 55 deletions.
42 changes: 37 additions & 5 deletions src/app/item-detail/item-detail.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,52 @@ <h2 class="card-title">{{itemData.overview.throughput > 1000 ?
</div>
</div>

<div class="col-sm" *ngIf="itemData.topMetricsSettings.percentile">
<div class="col-sm" *ngIf="itemData.topMetricsSettings.percentile || itemData.topMetricsSettings.percentile90">
<div class="card">
<div class="card-body overview-body">
<h2 *ngIf="itemData.overview.percentil >= 1000" class="card-title">{{
Math.round((itemData.overview.percentil / 1000) * 100) / 100}} <span class="unit-desc">s</span>
<h2 *ngIf="(itemData.overview.percentil || itemData.overview.percentile90) >= 1000" class="card-title">{{
Math.round(((itemData.overview.percentil || itemData.overview.percentile90) / 1000) * 100) / 100}} <span class="unit-desc">s</span>
</h2>
<h2 *ngIf="itemData.overview.percentil < 1000" class="card-title">{{
itemData.overview.percentil}} <span class="unit-desc">ms</span>
<h2 *ngIf="(itemData.overview.percentil || itemData.overview.percentile90) < 1000" class="card-title">{{
itemData.overview.percentil || itemData.overview.percentile90}} <span class="unit-desc">ms</span>
</h2>
</div>
<div class="card-footer bg-transparent card-footer-overview">90% Response time</div>
</div>
</div>


<div class="col-sm" *ngIf="itemData.topMetricsSettings.percentile95">
<div class="card">
<div class="card-body overview-body">
<h2 *ngIf="itemData.overview.percentile95 >= 1000" class="card-title">{{
Math.round((itemData.overview.percentile95 / 1000) * 100) / 100}} <span class="unit-desc">s</span>
</h2>
<h2 *ngIf="itemData.overview.percentile95 < 1000" class="card-title">{{
itemData.overview.percentile95}} <span class="unit-desc">ms</span>
</h2>
<h2 *ngIf="!itemData.overview.percentile95" class="card-title">N/A <span class="unit-desc">ms</span></h2>

</div>
<div class="card-footer bg-transparent card-footer-overview">95% Response time</div>
</div>
</div>

<div class="col-sm" *ngIf="itemData.topMetricsSettings.percentile99">
<div class="card">
<div class="card-body overview-body">
<h2 *ngIf="itemData.overview.percentile99 >= 1000" class="card-title">{{
Math.round((itemData.overview.percentile99 / 1000) * 100) / 100}} <span class="unit-desc">s</span>
</h2>
<h2 *ngIf="itemData.overview.percentile99 < 1000" class="card-title">{{
itemData.overview.percentile99}} <span class="unit-desc">ms</span>
</h2>
<h2 *ngIf="!itemData.overview.percentile99" class="card-title">N/A <span class="unit-desc">ms</span></h2>
</div>
<div class="card-footer bg-transparent card-footer-overview">99% Response time</div>
</div>
</div>

<div class="col-sm" *ngIf="itemData.topMetricsSettings.avgResponseTime">
<div class="card">
<div class="card-body overview-body">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ describe("RequestStatsCompareComponent", () => {
endDate: "",
startDate: "",
errorCount: 0,
percentil: 10,
percentil: null,
percentile90: 10,
percentile95: 10,
percentile99: 10,
errorRate: 0,
throughput: 100
},
Expand Down
23 changes: 8 additions & 15 deletions src/app/items.service.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ interface TopMetricsSettings {
avgResponseTime: boolean;
avgConnectionTime: boolean;
avgLatency: boolean;
percentile: boolean;
percentile: boolean; // legacy setting
percentile90: boolean;
percentile95: boolean;
percentile99: boolean
}

interface ItemOverview {
Expand All @@ -86,20 +89,10 @@ interface ItemOverview {
endDate: string;
errorRate: number;
maxVu: number;
percentil: number;
startDate: string;
throughput: number;
errorCount?: number;
}

interface ItemOverview {
avgLatency: number;
avgResponseTime: number;
duration: number;
endDate: string;
errorRate: number;
maxVu: number;
percentil: number;
percentil?: number; // legacy, it needs to be kept for backwards compatibility
percentile90: number;
percentile95: number;
percentile99: number;
startDate: string;
throughput: number;
errorCount?: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,23 @@ <h6>Test Report Top Statistics Bar</h6>
<label class="form-check-label" for="errorCount">Error Count</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="percentile"
formControlName="percentile" (change)="onCheckboxChange()">
<label class="form-check-label" for="percentile">90% Percentile</label>
<input type="checkbox" class="form-check-input" id="percentile90"
formControlName="percentile90" (change)="onCheckboxChange()">
<label class="form-check-label" for="percentile90">90% Percentile</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="percentile95"
formControlName="percentile95" (change)="onCheckboxChange()">
<label class="form-check-label" for="percentile95">95% Percentile</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="percentile99"
formControlName="percentile99" (change)="onCheckboxChange()">
<label class="form-check-label" for="percentile99">99% Percentile</label>
</div>



<div class="form-check">
<input type="checkbox" class="form-check-input" id="throughput"
formControlName="throughput" (change)="onCheckboxChange()">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@ import { UserService } from "../../../_services/user.service";
import { UserRole, Users } from "../../../_services/users.model";

@Component({
selector: "app-project-settings",
templateUrl: "./project-settings.component.html",
styleUrls: ["./project-settings.component.css"]
selector: 'app-project-settings',
templateUrl: './project-settings.component.html',
styleUrls: ['./project-settings.component.css']
})
export class ProjectSettingsComponent implements OnInit {

projectSettingsForm: FormGroup;
projectMembersForm: FormGroup;
projectMembers: FormArray;
projectMembersData
projectMembersData;
metricsEditable;
formControls = {
virtualUsers: null,
throughput: null,
percentile: null,
percentile90: null,
percentile95: null,
percentile99: null,
avgResponseTime: null,
avgConnectionTime: null,
avgLatency: null,
Expand All @@ -49,39 +51,44 @@ export class ProjectSettingsComponent implements OnInit {
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
ngOnInit() {}
ngOnInit() {
}

get usersFormArray() {
return this.projectMembersForm.controls.projectMembers as FormArray;
}

createFormControls(settings) {
this.formControls.virtualUsers = new FormControl(settings.topMetricsSettings.virtualUsers, []);
this.formControls.percentile = new FormControl(settings.topMetricsSettings.percentile, []);
this.formControls.percentile90 = new FormControl((settings.topMetricsSettings.percentile || settings.topMetricsSettings.percentile90), []);
this.formControls.percentile95 = new FormControl(settings.topMetricsSettings.percentile95 || false, []);
this.formControls.percentile99 = new FormControl(settings.topMetricsSettings.percentile99 || false, []);
this.formControls.throughput = new FormControl(settings.topMetricsSettings.throughput, []);
this.formControls.errorRate = new FormControl(settings.topMetricsSettings.errorRate, []);
this.formControls.errorCount = new FormControl(settings.topMetricsSettings.errorCount || false, []),
this.formControls.network = new FormControl(settings.topMetricsSettings.network, []);
this.formControls.network = new FormControl(settings.topMetricsSettings.network, []);
this.formControls.networkSent = new FormControl(settings.topMetricsSettings.networkSent || false, []);
this.formControls.networkReceived = new FormControl(settings.topMetricsSettings.networkReceived || false, []);
this.formControls.avgLatency = new FormControl(settings.topMetricsSettings.avgLatency, []);
this.formControls.avgConnectionTime = new FormControl(settings.topMetricsSettings.avgConnectionTime, []);
this.formControls.avgResponseTime = new FormControl(settings.topMetricsSettings.avgResponseTime, []);
this.formControls.scenarioUpsert = new FormControl(settings.upsertScenario, [])
this.formControls.scenarioUpsert = new FormControl(settings.upsertScenario, []);
this.formControls.projectName = new FormControl(settings.projectName, [
Validators.required,
Validators.maxLength(50),
Validators.minLength(3),
]);
this.formControls.projectMembers = new FormArray([])
this.formControls.projectMembers = new FormArray([]);
}

createForm() {
this.projectSettingsForm = new FormGroup({
virtualUsers: this.formControls.virtualUsers,
errorRate: this.formControls.errorRate,
errorCount: this.formControls.errorCount,
percentile: this.formControls.percentile,
percentile90: this.formControls.percentile90,
percentile95: this.formControls.percentile95,
percentile99: this.formControls.percentile99,
throughput: this.formControls.throughput,
network: this.formControls.network,
networkSent: this.formControls.networkSent,
Expand All @@ -95,7 +102,7 @@ export class ProjectSettingsComponent implements OnInit {
});
this.projectMembersForm = new FormGroup({
projectMembers: this.formControls.projectMembers,
})
});
}

open(content) {
Expand All @@ -104,9 +111,9 @@ export class ProjectSettingsComponent implements OnInit {

if (role === "admin") {
this.userService.fetchUsers().subscribe((users) => {
this.mapProjectMembersToUsersData(r.body.projectMembers, users)
this.addCheckboxes()
})
this.mapProjectMembersToUsersData(r.body.projectMembers, users);
this.addCheckboxes();
});
}

this.createFormControls(r.body);
Expand All @@ -123,15 +130,17 @@ export class ProjectSettingsComponent implements OnInit {
const projectMembers = this.projectMembersData
.filter(member => member.role !== UserRole.Admin)
.filter(member => member.isMember)
.map(member => member.id)
.map(member => member.id);

const payload = {
projectName: this.formControls.projectName.value,
upsertScenario: this.formControls.scenarioUpsert.value,
topMetricsSettings: {
virtualUsers: this.formControls.virtualUsers.value,
errorRate: this.formControls.errorRate.value,
percentile: this.formControls.percentile.value,
percentile90: this.formControls.percentile90.value,
percentile95: this.formControls.percentile95.value,
percentile99: this.formControls.percentile99.value,
throughput: this.formControls.throughput.value,
network: this.formControls.network.value,
avgLatency: this.formControls.avgLatency.value,
Expand All @@ -151,15 +160,16 @@ export class ProjectSettingsComponent implements OnInit {
this.projectService.loadProjects();
return this.projectApiService.setData(message);
});
this.projectSettingsForm.reset()
this.projectSettingsForm.reset();
this.modalService.dismissAll();
}
}

isEditable() {
const enabledMetrics = Object.values([this.formControls.virtualUsers, this.formControls.errorRate, this.formControls.percentile,
this.formControls.throughput, this.formControls.network, this.formControls.avgLatency, this.formControls.avgResponseTime,
this.formControls.avgConnectionTime, this.formControls.errorCount, this.formControls.networkSent, this.formControls.networkReceived])
const enabledMetrics = Object.values([this.formControls.virtualUsers, this.formControls.errorRate, this.formControls.percentile90,
this.formControls.percentile95, this.formControls.percentile99, this.formControls.throughput, this.formControls.network,
this.formControls.avgLatency, this.formControls.avgResponseTime,
this.formControls.avgConnectionTime, this.formControls.errorCount, this.formControls.networkSent, this.formControls.networkReceived])
.map(control => control.value).filter(value => value === true);
this.metricsEditable = enabledMetrics.length > 5;
}
Expand All @@ -171,17 +181,17 @@ export class ProjectSettingsComponent implements OnInit {
private mapProjectMembersToUsersData = (projectMembers: string[], users: Users[]) => {
this.projectMembersData = users
.map(user => {
const isMember = projectMembers.find(member => member === user.id)
return {
id: user.id,
username: user.username,
isMember: user.role === UserRole.Admin ? true : !!isMember,
role: user.role,
isDisabled: user.role === UserRole.Admin
}
})
const isMember = projectMembers.find(member => member === user.id);
return {
id: user.id,
username: user.username,
isMember: user.role === UserRole.Admin ? true : !!isMember,
role: user.role,
isDisabled: user.role === UserRole.Admin
};
});

}
};

private addCheckboxes() {
this.projectMembersData.forEach((projectMember) => this.usersFormArray.push(new FormControl(projectMember.isMember)));
Expand Down

0 comments on commit 75ecc3a

Please sign in to comment.