Skip to content

Commit 24de28d

Browse files
authored
Affected Tests Amount and Test Runs columns on Issues list (#165)
* Error handling for Last Audit Created Date * feat: Added affected tests column to the issues list * Added test runs into filter * linting and formatting * testing fixes * single quote formatting
1 parent ba72cd5 commit 24de28d

File tree

5 files changed

+128
-60
lines changed

5 files changed

+128
-60
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aquality-tracking-ui",
3-
"version": "1.4.1",
3+
"version": "1.4.2",
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",

src/app/pages/audit/audit-list/audit.component.ts

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,23 @@ export class AuditComponent implements OnInit {
1919
auditors: User[];
2020
auditsList: Audit[];
2121
defSort = { property: 'last_created_due_date', order: TFOrder.desc };
22-
rowColors: any[] = [{
23-
property: 'last_created_due_date',
24-
color: 'warning',
25-
higher: new Date(new Date(new Date().setDate(new Date().getDate() - 1)).setHours(0, 0, 0, 0)),
26-
lower: new Date(new Date(new Date().setDate(new Date().getDate() + 15)).setHours(0, 0, 0, 0))
27-
}, {
28-
property: 'hasOpened',
29-
color: 'none'
30-
}, {
31-
property: 'last_created_due_date',
32-
color: 'danger',
33-
lower: new Date(new Date(new Date().setDate(new Date().getDate())).setHours(0, 0, 0, 0))
34-
}];
22+
rowColors: Object[] = [
23+
{
24+
property: 'last_created_due_date',
25+
color: 'warning',
26+
higher: new Date(new Date(new Date().setDate(new Date().getDate() - 1)).setHours(0, 0, 0, 0)),
27+
lower: new Date(new Date(new Date().setDate(new Date().getDate() + 15)).setHours(0, 0, 0, 0))
28+
},
29+
{
30+
property: 'hasOpened',
31+
color: 'none'
32+
},
33+
{
34+
property: 'last_created_due_date',
35+
color: 'danger',
36+
lower: new Date(new Date(new Date().setDate(new Date().getDate())).setHours(0, 0, 0, 0))
37+
}
38+
];
3539
columns: TFColumn[];
3640
linkNames: string[] = [];
3741

@@ -41,7 +45,7 @@ export class AuditComponent implements OnInit {
4145
public auditService: AuditService,
4246
public userService: UserService,
4347
private permissions: PermissionsService
44-
) { }
48+
) {}
4549

4650
async ngOnInit() {
4751
const isAuditAdmin = await this.permissions.hasPermissions([EGlobalPermissions.audit_admin]);
@@ -51,27 +55,36 @@ export class AuditComponent implements OnInit {
5155
isAuditAdmin ? this.linkNames.push('Create New') : this.linkNames.push('Not created');
5256
this.stats.forEach(async (stat) => {
5357
if (stat.last_submitted_date || stat.last_created_due_date) {
54-
stat.last_created_due_date = stat.last_submitted_id !== stat.last_created_id
55-
? new Date(stat.last_created_due_date)
56-
: this.auditService.createDueDate(new Date(stat.last_submitted_date));
58+
stat.last_created_due_date =
59+
stat.last_submitted_id !== stat.last_created_id
60+
? new Date(stat.last_created_due_date)
61+
: this.auditService.createDueDate(new Date(stat.last_submitted_date));
5762
} else {
5863
stat.last_created_due_date = this.auditService.createDueDate(new Date(stat.created));
5964
}
6065

61-
if (stat.last_created_id && (stat.last_submitted_id !== stat.last_created_id)) {
66+
if (stat.last_created_id && stat.last_submitted_id !== stat.last_created_id) {
6267
stat['next_action'] = { text: stat.status_name, link: `/audit/${stat.id}/info/${stat.last_created_id}` };
63-
if (stat.status_name !== 'Open') { stat['hasOpened'] = true; }
64-
if (!this.linkNames.includes(stat.status_name) && stat.status_name) { this.linkNames.push(stat.status_name); }
68+
if (stat.status_name !== 'Open') {
69+
stat['hasOpened'] = true;
70+
}
71+
if (!this.linkNames.includes(stat.status_name) && stat.status_name) {
72+
this.linkNames.push(stat.status_name);
73+
}
6574
} else if (isAuditAdmin) {
6675
stat['next_action'] = { text: 'Create New', link: `/audit/${stat.id}/create` };
6776
} else {
6877
stat['next_action'] = { text: 'Not created' };
6978
}
7079

71-
this.auditsList = await this.auditService.getAudits({ project: { id: stat.id } } );
72-
let datesArray: (Date|string|number)[] = [];
73-
this.auditsList.forEach(audit => {datesArray.push(audit.created)});
74-
stat.last_audit_created_date = new Date(Math.max.apply(null, datesArray));
80+
this.auditsList = await this.auditService.getAudits({ project: { id: stat.id } });
81+
const datesArray: (Date | string | number)[] = [];
82+
this.auditsList.forEach((audit) => {
83+
datesArray.push(audit.created);
84+
});
85+
stat.last_audit_created_date = isFinite(Math.max.apply(null, datesArray))
86+
? new Date(Math.max.apply(null, datesArray))
87+
: undefined;
7588
});
7689
this.services = await this.auditService.getServices();
7790
this.createColumns();
@@ -99,7 +112,7 @@ export class AuditComponent implements OnInit {
99112
type: TFColumnType.autocomplete,
100113
lookup: {
101114
propToShow: ['first_name', 'second_name'],
102-
values: this.coordinators,
115+
values: this.coordinators
103116
},
104117
class: 'ft-width-150'
105118
},
@@ -151,7 +164,7 @@ export class AuditComponent implements OnInit {
151164
type: TFColumnType.multiselect,
152165
lookup: {
153166
propToShow: ['first_name', 'second_name'],
154-
values: this.auditors,
167+
values: this.auditors
155168
},
156169
class: 'ft-width-250'
157170
},
@@ -185,10 +198,10 @@ export class AuditComponent implements OnInit {
185198
type: TFColumnType.multiselect,
186199
lookup: {
187200
propToShow: ['first_name', 'second_name'],
188-
values: this.auditors,
201+
values: this.auditors
189202
},
190203
class: 'ft-width-250'
191-
},
204+
}
192205
];
193206
}
194207
}

src/app/pages/project/issue/issue-list/issue-list.component.ts

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,38 @@ import { Router, ActivatedRoute } from '@angular/router';
33
import { Issue } from '../../../../shared/models/issue';
44
import { Label } from '../../../../shared/models/general';
55
import { User } from '../../../../shared/models/user';
6+
import { TestRun } from 'src/app/shared/models/testrun';
67
import { UserService } from 'src/app/services/user/user.services';
78
import { IssueService } from 'src/app/services/issue/issue.service';
8-
import { PermissionsService, EGlobalPermissions, ELocalPermissions } from 'src/app/services/permissions/current-permissions.service';
9+
import { ProjectService } from '../../../../services/project/project.service';
10+
import { TestResultService } from 'src/app/services/test-result/test-result.service';
11+
import { TestRunService } from 'src/app/services/testrun/testrun.service';
12+
import {
13+
PermissionsService,
14+
EGlobalPermissions,
15+
ELocalPermissions
16+
} from 'src/app/services/permissions/current-permissions.service';
917
import { ResultResolutionService } from 'src/app/services/result-resolution/result-resolution.service';
1018
import { TFColumn, TFSorting, TFOrder, TFColumnType } from 'src/app/elements/table-filter/tfColumn';
1119
import { ResultResolution } from 'src/app/shared/models/result-resolution';
1220
import { LocalPermissions } from 'src/app/shared/models/local-permissions';
13-
import {ProjectService} from '../../../../services/project/project.service';
1421

1522
@Component({
1623
templateUrl: './issue-list.component.html',
1724
styleUrls: ['./issue-list.component.scss']
1825
})
1926
export class IssueListComponent implements OnInit {
20-
2127
constructor(
2228
public userService: UserService,
2329
private router: Router,
2430
private route: ActivatedRoute,
2531
private issueService: IssueService,
2632
private permissions: PermissionsService,
2733
private resolutionService: ResultResolutionService,
28-
private projectService: ProjectService
29-
) { }
34+
private projectService: ProjectService,
35+
private testResultService: TestResultService,
36+
private testRunService: TestRunService
37+
) {}
3038

3139
projectId: number;
3240
issues: Issue[];
@@ -40,32 +48,51 @@ export class IssueListComponent implements OnInit {
4048
defSort: TFSorting = { property: 'created', order: TFOrder.asc };
4149
hideCreateModal = true;
4250
isAiOn: boolean;
51+
testRuns: TestRun[];
4352

4453
async ngOnInit() {
4554
this.projectId = this.route.snapshot.params.projectId;
4655
[this.issues, this.resolutions, this.canEdit, this.projectUsers, this.statuses, this.isAiOn] = await Promise.all([
4756
this.issueService.getIssues({ project_id: this.projectId }),
4857
this.resolutionService.getResolution(this.projectId),
49-
this.permissions.hasProjectPermissions(this.projectId,
50-
[EGlobalPermissions.manager], [ELocalPermissions.manager, ELocalPermissions.engineer]),
58+
this.permissions.hasProjectPermissions(
59+
this.projectId,
60+
[EGlobalPermissions.manager],
61+
[ELocalPermissions.manager, ELocalPermissions.engineer]
62+
),
5163
this.userService.getProjectUsers(this.projectId),
5264
this.issueService.getIssueStatuses(),
53-
((await this.projectService.getProject(this.projectId)).ai_resolutions === 1)
65+
(await this.projectService.getProject(this.projectId)).ai_resolutions === 1
5466
]);
55-
this.projectUsers = this.projectUsers.filter(user => user.admin === 1 || user.manager === 1 || user.engineer === 1);
56-
this.users = this.projectUsers.map(x => x.user);
67+
this.projectUsers = this.projectUsers.filter(
68+
(user) => user.admin === 1 || user.manager === 1 || user.engineer === 1
69+
);
70+
this.users = this.projectUsers.map((x) => x.user);
71+
this.testRuns = await this.testRunService.getTestRun({ project_id: this.projectId });
5772
this.addLinks();
73+
this.addAffectedTestsAndRuns();
5874
this.createColumns();
5975
}
6076

6177
async addLinks() {
62-
this.issues.forEach(issue => {
63-
issue['external_link'] = issue.external_url
64-
? { text: 'Open', link: issue.external_url }
65-
: {};
78+
this.issues.forEach((issue) => {
79+
issue['external_link'] = issue.external_url ? { text: 'Open', link: issue.external_url } : {};
6680
});
6781
}
6882

83+
async addAffectedTestsAndRuns() {
84+
const testResults = await this.testResultService.getTestResultsStat(this.projectId, null, null);
85+
for (const issue of this.issues) {
86+
const affectedTestsArray = testResults.filter((result) => Number(result.issue_id) === issue.id);
87+
issue['affected_tests_amount'] = affectedTestsArray.length;
88+
issue['test_runs'] = [];
89+
for (const test of affectedTestsArray) {
90+
issue['test_runs'].push(this.testRuns.find((run) => run.id === test.test_run_id));
91+
}
92+
issue['test_runs'] = [...new Set(issue['test_runs'])];
93+
}
94+
}
95+
6996
async updateIssue(issue: Issue) {
7097
if (issue.resolution) {
7198
issue.resolution_id = issue.resolution.id;
@@ -87,7 +114,7 @@ export class IssueListComponent implements OnInit {
87114
this.issueService.getAiIssues(this.projectId);
88115
}
89116

90-
async execute(result: {executed: boolean, result?: Issue}) {
117+
async execute(result: { executed: boolean; result?: Issue }) {
91118
this.hideCreateModal = true;
92119
if (result.executed) {
93120
await this.updateList();
@@ -99,12 +126,13 @@ export class IssueListComponent implements OnInit {
99126
}
100127

101128
rowClicked(issue: Issue) {
102-
return this.router.navigate(['/project/' + this.route.snapshot.params['projectId'] + '/issue/' + issue.id]);
129+
return this.router.navigate([`/project/${this.route.snapshot.params['projectId']}/issue/${issue.id}`]);
103130
}
104131

105132
private async updateList() {
106133
this.issues = await this.issueService.getIssues({ project_id: this.projectId });
107134
this.addLinks();
135+
this.addAffectedTestsAndRuns();
108136
}
109137

110138
private createColumns() {
@@ -114,8 +142,9 @@ export class IssueListComponent implements OnInit {
114142
property: 'id',
115143
sorting: true,
116144
type: TFColumnType.text,
117-
class: 'fit',
118-
}, {
145+
class: 'fit'
146+
},
147+
{
119148
name: 'Status',
120149
property: 'status',
121150
filter: true,
@@ -127,7 +156,8 @@ export class IssueListComponent implements OnInit {
127156
propToShow: ['name']
128157
},
129158
class: 'fit'
130-
}, {
159+
},
160+
{
131161
name: 'Resolution',
132162
property: 'resolution',
133163
filter: true,
@@ -139,7 +169,8 @@ export class IssueListComponent implements OnInit {
139169
propToShow: ['name']
140170
},
141171
class: 'fit'
142-
}, {
172+
},
173+
{
143174
name: 'Title',
144175
property: 'title',
145176
filter: true,
@@ -150,7 +181,29 @@ export class IssueListComponent implements OnInit {
150181
creationLength: 500,
151182
required: true
152183
}
153-
}, {
184+
},
185+
{
186+
name: 'Affected Tests Amount',
187+
property: 'affected_tests_amount',
188+
sorting: true,
189+
type: TFColumnType.number,
190+
class: 'ft-width-175'
191+
},
192+
{
193+
name: 'Test Runs',
194+
property: 'test_runs',
195+
filter: true,
196+
type: TFColumnType.multiselect,
197+
lookup: {
198+
propToShow: ['build_name'],
199+
values: this.testRuns
200+
},
201+
editable: false,
202+
bulkEdit: true,
203+
sorting: true,
204+
class: 'ft-width-250'
205+
},
206+
{
154207
name: 'Assignee',
155208
property: 'assignee',
156209
type: TFColumnType.autocomplete,
@@ -163,17 +216,19 @@ export class IssueListComponent implements OnInit {
163216
propToShow: ['first_name', 'second_name']
164217
},
165218
class: 'fit'
166-
}, {
219+
},
220+
{
167221
name: 'Created',
168222
property: 'created',
169223
type: TFColumnType.date,
170224
class: 'fit'
171-
}, {
225+
},
226+
{
172227
name: 'External Issue',
173228
property: 'external_link',
174229
type: TFColumnType.externalLink,
175230
class: 'ft-width-250'
176-
},
231+
}
177232
];
178233

179234
this.hiddenColumns = [
@@ -182,12 +237,13 @@ export class IssueListComponent implements OnInit {
182237
property: 'description',
183238
type: TFColumnType.longtext,
184239
class: 'ft-width-250'
185-
}, {
240+
},
241+
{
186242
name: 'Expression',
187243
property: 'expression',
188244
type: TFColumnType.text,
189245
class: 'ft-width-250'
190-
},
246+
}
191247
];
192248
}
193249
}

src/app/shared/models/test-result.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ export class TestResultStat {
3333
name?: string;
3434
status?: string;
3535
resolution?: string;
36-
assignee?: string;
37-
developer?: string;
38-
comment?: string;
36+
issue_id?: string;
37+
issue_title?: string;
3938
}
4039

4140
export class TestResultAttachment {

0 commit comments

Comments
 (0)