Skip to content

Commit 188ca6e

Browse files
Save work
1 parent 8b4db6e commit 188ca6e

File tree

9 files changed

+386
-54
lines changed

9 files changed

+386
-54
lines changed

e2e/specs/administration/permissions.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import projects from '../../data/projects.json';
1010
import { notFound } from '../../pages/notFound.po';
1111

1212
const editorExamples = {
13-
admin: usersTestData.autoAdmin,
13+
autoAdmin: usersTestData.autoAdmin,
1414
localAdmin: usersTestData.localAdmin,
1515
localManager: usersTestData.localManager,
1616
manager: usersTestData.manager
@@ -27,7 +27,7 @@ describe('Administartion:', () => {
2727

2828
beforeAll(async () => {
2929
await projectHelper.init({
30-
admin: usersTestData.autoAdmin,
30+
autoAdmin: usersTestData.autoAdmin,
3131
localAdmin: usersTestData.localAdmin,
3232
localManager: usersTestData.localManager,
3333
localEngineer: usersTestData.localEngineer,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
.slc-part {
2+
margin: 15px 0 0 0;
3+
display: inline-block;
4+
height: 20px;
5+
width: 100%;
6+
-webkit-transform: translateZ(0);
7+
transform: translateZ(0);
8+
-webkit-backface-visibility: hidden;
9+
backface-visibility: hidden;
10+
-moz-osx-font-smoothing: grayscale;
11+
position: relative;
12+
}
13+
14+
.slc-part:before {
15+
content: "";
16+
position: absolute;
17+
opacity: .4;
18+
z-index: -1;
19+
left: 50%;
20+
right: 50%;
21+
top: 50%;
22+
bottom:50%;
23+
background-color: inherit;
24+
border-top-right-radius: inherit;
25+
border-bottom-right-radius: inherit;
26+
border-top-left-radius: inherit;
27+
border-bottom-left-radius: inherit;
28+
filter: brightness(120%);
29+
-webkit-transition-property: left right;
30+
transition-property: left right;
31+
-webkit-transition-duration: 0.3s;
32+
transition-duration: 0.3s;
33+
-webkit-transition-timing-function: ease-out;
34+
transition-timing-function: ease-out;
35+
}
36+
37+
.slc-part:hover:before {
38+
top: -3;
39+
bottom:-3;
40+
left: -3;
41+
right: -3;
42+
}
43+
44+
.slc-part:first-child {
45+
border-top-left-radius: 10px;
46+
border-bottom-left-radius: 10px;
47+
}
48+
49+
.slc-part:last-child {
50+
border-top-right-radius: 10px;
51+
border-bottom-right-radius: 10px;
52+
}
53+
54+
.slc-part:hover .slc-title {
55+
opacity: 1;
56+
}
57+
58+
.slc-title {
59+
transition: all .3s ease-in;
60+
opacity: 0;
61+
margin-left: 10px;
62+
bottom: -25px;
63+
position: absolute;
64+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div class="row">
2+
<div class="col-sm-12">
3+
<div *ngFor="let item of dataToShow" class="slc-part" [style.width.%]="item.percent" [style.background-color]="item.color">
4+
<div class="slc-title text-centered">
5+
{{item.percent | number: '1.0-1'}}% {{item.label}}
6+
</div>
7+
</div>
8+
</div>
9+
</div>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Component, OnInit, Input } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-single-line-bar-chart',
5+
templateUrl: './single-line-bar-chart.component.html',
6+
styleUrls: ['./single-line-bar-chart.component.css']
7+
})
8+
export class SingleLineBarChartComponent implements OnInit {
9+
10+
@Input() data: SingleLineBarChartData[];
11+
dataToShow: SingleLineBarChartData[];
12+
13+
constructor() { }
14+
15+
ngOnInit() {
16+
this.updateData();
17+
}
18+
19+
ngOnChanges() {
20+
this.updateData()
21+
}
22+
23+
updateData() {
24+
if (this.data) {
25+
const sum = this.data.map(item => item.value).reduce((prev, next) => prev + next);
26+
this.data.forEach(element => {
27+
element['percent'] = (element.value / sum) * 100;
28+
});
29+
30+
this.dataToShow = this.data.filter(x => x.value !== 0)
31+
}
32+
}
33+
34+
}
35+
36+
export class SingleLineBarChartData {
37+
value: number;
38+
color: string;
39+
label: string;
40+
}

src/app/pages/project/project-view/project-veiw.component.ts

Lines changed: 156 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,112 @@
11
import { Component, OnInit } from '@angular/core';
22
import { Router, ActivatedRoute } from '@angular/router';
3-
import { SimpleRequester } from '../../../services/simple-requester';
43
import { TestRun } from '../../../shared/models/testRun';
54
import { Project } from '../../../shared/models/project';
65
import { TestRunService } from '../../../services/testRun.service';
76
import { ProjectService } from '../../../services/project.service';
8-
import { TestSuiteService } from '../../../services/testSuite.service';
97
import { TestRunStat } from '../../../shared/models/testrunStats';
108
import { AuditService } from '../../../services/audits.service';
119
import { Audit, AuditNotification } from '../../../shared/models/audit';
1210
import { GlobalDataService } from '../../../services/globaldata.service';
1311
import { TFColumn, TFColumnType } from '../../../elements/table/tfColumn';
12+
import { IssueService } from '../../../services/issue.service';
13+
import { Issue } from '../../../shared/models/issue';
14+
import { SingleLineBarChartData } from '../../../elements/single-line-bar-chart/single-line-bar-chart.component';
15+
import { TestSuiteService } from '../../../services/testSuite.service';
16+
import { TestSuite } from '../../../shared/models/testSuite';
1417

1518
@Component({
1619
templateUrl: './project-view.component.html',
17-
providers: [
18-
TestRunService,
19-
SimpleRequester,
20-
ProjectService,
21-
TestSuiteService,
22-
AuditService
23-
]
20+
styleUrls: ['./project-view.component.css']
2421
})
2522
export class ProjectViewComponent implements OnInit {
2623
audits: Audit[];
24+
suites: TestSuite[];
2725
project: Project;
2826
testRuns: TestRun[];
2927
testRun: TestRun;
3028
testRunStats: TestRunStat[];
3129
hideAll = true;
3230
auditNotification: AuditNotification;
3331
notification: { text: string, type: string };
34-
columns: TFColumn[];
32+
testRunColumns: TFColumn[] = [
33+
{ name: 'Build Name', property: 'build_name', type: TFColumnType.text },
34+
{ name: 'Execution Environment', property: 'execution_environment', type: TFColumnType.text },
35+
{ name: 'Start Time', property: 'start_time', type: TFColumnType.date, class: 'fit' },
36+
{ name: 'Finish Time', property: 'finish_time', type: TFColumnType.date, class: 'fit' },
37+
{ name: 'Pass Rate', property: 'failed', type: TFColumnType.percent, class: 'fit' },
38+
];
39+
issueColumns: TFColumn[] = [{ name: 'Id', property: 'id', type: TFColumnType.text, class: 'fit' },
40+
{ name: 'Title', property: 'title', type: TFColumnType.text },
41+
{
42+
name: 'Status', property: 'status', type: TFColumnType.colored,
43+
lookup: {
44+
values: [],
45+
propToShow: ['name']
46+
}, class: 'fit'
47+
}, {
48+
name: 'Resolution', property: 'resolution', type: TFColumnType.colored,
49+
lookup: {
50+
values: [],
51+
propToShow: ['name']
52+
}, class: 'fit'
53+
}];
54+
allIssues: Issue[];
55+
myIssues: Issue[];
56+
quality: { current: { quality: number, NA: number, testIssue: number, other: number, appIssue: number, passed: number, total: number }, average: number };
57+
chartData: SingleLineBarChartData[];
58+
issueStat: { open: number, openApp: number, total: number, totalApp: number };
3559

3660
constructor(
3761
public globaldata: GlobalDataService,
3862
private projectService: ProjectService,
3963
private testrunService: TestRunService,
64+
private issueService: IssueService,
4065
private auditService: AuditService,
66+
private testSuiteService: TestSuiteService,
4167
private route: ActivatedRoute,
4268
private router: Router
4369
) { }
4470

4571
async ngOnInit() {
46-
this.testRun = { project_id: this.route.snapshot.params['projectId'], debug: 0 };
47-
this.project = { id: this.route.snapshot.params['projectId'] };
48-
49-
this.projectService.getProjects(this.project).subscribe(async (projects) => {
50-
this.project = projects[0];
51-
if (this.globaldata.auditModule) {
52-
this.auditService.getAudits({ project: { id: this.project.id } }).subscribe(audits => {
53-
this.audits = audits;
54-
this.generateAuditNotification();
55-
this.setAuditNotification();
56-
});
57-
}
58-
this.testRuns = await this.testrunService.getTestRun(this.testRun, 5);
59-
this.testRunStats = await this.testrunService.getTestsRunStats(this.testRun);
60-
this.testRuns.forEach(testrun => {
61-
testrun['failed'] = this.getNumberOfFails(testrun.id);
72+
const projectId = this.route.snapshot.params.projectId;
73+
this.testRun = { project_id: projectId, debug: 0 };
74+
this.project = { id: projectId };
75+
76+
this.allIssues = await this.issueService.getIssues({
77+
project_id: projectId
78+
});
79+
80+
this.myIssues = this.allIssues.filter(x => x.status_id != 4 && x.status_id != 2 && x.assignee_id === this.globaldata.currentUser.id);
81+
82+
this.suites = await this.testSuiteService.getTestSuite({ project_id: projectId });
83+
this.project = (await this.projectService.getProjects(this.project).toPromise())[0];
84+
85+
if (this.globaldata.auditModule) {
86+
this.auditService.getAudits({ project: { id: this.project.id } }).subscribe(audits => {
87+
this.audits = audits;
88+
this.generateAuditNotification();
89+
this.setAuditNotification();
6290
});
63-
this.columns = [
64-
{ name: 'Build Name', property: 'build_name', type: TFColumnType.text },
65-
{ name: 'Execution Environment', property: 'execution_environment', type: TFColumnType.text },
66-
{ name: 'Start Time', property: 'start_time', type: TFColumnType.date, class: 'fit' },
67-
{ name: 'Finish Time', property: 'finish_time', type: TFColumnType.date, class: 'fit' },
68-
{ name: 'Pass Rate', property: 'failed', type: TFColumnType.percent, class: 'fit' },
69-
];
91+
}
92+
93+
this.testRuns = await this.testrunService.getTestRun(this.testRun, 5);
94+
this.testRunStats = await this.testrunService.getTestsRunStats(this.testRun);
95+
this.testRuns.forEach(testrun => {
96+
testrun['failed'] = this.getNumberOfFails(testrun.id);
7097
});
98+
this.quality = this.getQualityInfo(this.suites, this.testRunStats);
99+
this.chartData = this.createChartData(this.quality.current);
100+
this.issueStat = this.getIssuesStat(this.allIssues);
101+
}
102+
103+
getIssuesStat(issues: Issue[]): { open: number, openApp: number, total: number, totalApp: number } {
104+
return {
105+
open: issues.filter(x => x.status_id != 4 && x.status_id != 2).length,
106+
openApp: issues.filter(x => x.status_id != 4 && x.status_id != 2 && x.resolution.color === 1).length,
107+
total: issues.length,
108+
totalApp: issues.filter(x => x.resolution.color === 1).length
109+
}
71110
}
72111

73112
getNumberOfFails(id: number) {
@@ -163,4 +202,88 @@ export class ProjectViewComponent implements OnInit {
163202
const difference_ms = Math.abs(date1_ms - date2_ms);
164203
return Math.round(difference_ms / ONEDAY);
165204
}
205+
206+
getQualityInfo(suites: TestSuite[], testRunStats: TestRunStat[]): { current: { quality: number, NA: number, testIssue: number, other: number, appIssue: number, passed: number, total: number }, average: number } {
207+
const testRunStatsBySuite = [];
208+
for (const suite of suites) {
209+
const teStats: TestRunStat[] = testRunStats.filter(stat => stat.test_suite_id === suite.id);
210+
testRunStatsBySuite.push({ suite: suite, stats: teStats });
211+
}
212+
213+
return {
214+
current: this.calculateAutomationQuality(testRunStatsBySuite),
215+
average: this.calculateAverageAutomationQuality(testRunStatsBySuite)
216+
};
217+
}
218+
219+
calculateAutomationQuality(testRunStatsBySuites: { suite: TestSuite, stats: TestRunStat[] }[]): { quality: number, NA: number, testIssue: number, other: number, appIssue: number, passed: number, total: number } {
220+
let stat = { quality: 0, NA: 0, testIssue: 0, other: 0, appIssue: 0, passed: 0, total: 0 }
221+
222+
for (const suiteStat of testRunStatsBySuites) {
223+
if (suiteStat.stats[0]) {
224+
stat.NA += suiteStat.stats[0].not_assigned;
225+
stat.appIssue += suiteStat.stats[0].app_issue;
226+
stat.passed += suiteStat.stats[0].passed;
227+
stat.testIssue += suiteStat.stats[0].warning;
228+
stat.other += suiteStat.stats[0].other;
229+
stat.total += suiteStat.stats[0].total;
230+
}
231+
}
232+
233+
stat.quality = stat.total > 0 ? (1 - ((stat.NA * 1 + stat.testIssue * 0.75 + stat.other * 0.5) / (stat.total))) * 100 : 0;
234+
if (stat.quality < 0) {
235+
stat.quality = 0;
236+
}
237+
238+
return stat;
239+
}
240+
241+
calculateAverageAutomationQuality(testRunStatsBySuite: { suite: TestSuite, stats: TestRunStat[] }[]): number {
242+
let ttlNA = 0;
243+
let ttltestIssue = 0;
244+
let ttlOther = 0;
245+
let ttl = 0;
246+
let averageAutomationQuality: number;
247+
248+
for (const suiteStat of testRunStatsBySuite) {
249+
for (const stat of suiteStat.stats) {
250+
if (Math.floor(new Date().getTime() - new Date(stat.finish_time).getTime()) / (1000 * 60 * 60 * 24) < 90) {
251+
ttlNA += stat.not_assigned;
252+
ttltestIssue += stat.warning;
253+
ttlOther += stat.other;
254+
ttl += stat.total;
255+
}
256+
}
257+
}
258+
averageAutomationQuality = ttl > 0 ? (1 - ((ttlNA * 1 + ttltestIssue * 0.75 + ttlOther * 0.5) / (ttl))) * 100 : 0;
259+
if (averageAutomationQuality < 0) {
260+
averageAutomationQuality = 0;
261+
}
262+
263+
return averageAutomationQuality;
264+
}
265+
266+
createChartData(data: { quality: number, NA: number, testIssue: number, other: number, appIssue: number, passed: number }) {
267+
return [{
268+
value: data.passed,
269+
color: '#28A745',
270+
label: 'Passed'
271+
}, {
272+
value: data.appIssue,
273+
color: '#DC3545',
274+
label: 'Application Issue'
275+
}, {
276+
value: data.testIssue,
277+
color: '#FFC107',
278+
label: 'Test Issue'
279+
}, {
280+
value: data.other,
281+
color: '#17A2B8',
282+
label: 'Other'
283+
}, {
284+
value: data.NA,
285+
color: '#007BFF',
286+
label: 'Not Assigned'
287+
}];
288+
}
166289
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.row {
2+
margin-bottom: 80px;
3+
}

0 commit comments

Comments
 (0)