Skip to content

Commit cd9075e

Browse files
authored
Merge branch 'dev' into release/v2.2.1
2 parents c844df2 + c2c5666 commit cd9075e

File tree

21 files changed

+196
-43
lines changed

21 files changed

+196
-43
lines changed

framework/python/src/common/testreport.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,12 @@ def to_html(self):
284284

285285
module_reports = self._module_reports
286286
env_module = Environment(loader=BaseLoader())
287-
pages_num = self._pages_num(json_data)
287+
manufacturer_length = len(json_data['device']['manufacturer'])
288+
device_name_length = len(json_data['device']['model'])
289+
title_length = manufacturer_length + device_name_length +1
290+
tests_first_page = self._calculate_tests_first_page(title_length)
291+
pages_num = self._pages_num(json_data, tests_first_page)
292+
288293
module_templates = [
289294
env_module.from_string(s).render(
290295
name=current_test_pack.name,
@@ -310,11 +315,28 @@ def to_html(self):
310315
steps_to_resolve=steps_to_resolve_,
311316
module_reports=module_reports,
312317
pages_num=pages_num,
313-
tests_first_page=TESTS_FIRST_PAGE,
318+
tests_first_page=tests_first_page,
314319
tests_per_page=TESTS_PER_PAGE,
315320
module_templates=module_templates
316321
))
317322

323+
def _calculate_tests_first_page(self, title_length):
324+
# Calculation of test results lines at first page
325+
326+
# Average chars per line is 25
327+
estimated_lines = title_length // 25
328+
if title_length % 25 > 0:
329+
estimated_lines += 1
330+
331+
if estimated_lines > 1:
332+
# Line height is 60 px
333+
title_px = (estimated_lines - 1) * 60
334+
available_space_px = 445 - title_px
335+
estimated_tests_first_page = available_space_px // 39
336+
return min(estimated_tests_first_page, TESTS_FIRST_PAGE)
337+
else:
338+
return TESTS_FIRST_PAGE
339+
318340
def _add_page_counter(self, html):
319341
# Add page nums and total page
320342
soup = BeautifulSoup(html, features='html5lib')
@@ -324,18 +346,18 @@ def _add_page_counter(self, html):
324346
div.string = f'Page {index+1}/{total_pages}'
325347
return str(soup)
326348

327-
def _pages_num(self, json_data):
349+
def _pages_num(self, json_data, tests_first_page=TESTS_FIRST_PAGE):
328350

329351
# Calculate pages
330352
test_count = len(json_data['tests']['results'])
331353

332354
# Multiple pages required
333-
if test_count > TESTS_FIRST_PAGE:
355+
if test_count > tests_first_page:
334356
# First page
335357
pages = 1
336358

337359
# Remaining testsgenerate
338-
test_count -= TESTS_FIRST_PAGE
360+
test_count -= tests_first_page
339361
pages += (int)(test_count / TESTS_PER_PAGE)
340362
pages = pages + 1 if test_count % TESTS_PER_PAGE > 0 else pages
341363

framework/python/src/core/session.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,9 @@ def add_test_result(self, result):
453453
if len(result.description) != 0:
454454
test_result.description = result.description
455455

456+
# Add details to test result
457+
test_result.details = result.details
458+
456459
# Add recommendations if provided
457460
if result.recommendations is not None:
458461
test_result.recommendations = result.recommendations

framework/python/src/test_orc/test_case.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ class TestCase: # pylint: disable=too-few-public-methods,too-many-instance-attr
2828
result: str = TestResult.NON_COMPLIANT
2929
recommendations: list = field(default_factory=lambda: [])
3030
optional_recommendations: list = field(default_factory=lambda: [])
31+
details: str = ""
3132

3233
def to_dict(self):
3334

3435
test_dict = {
3536
"name": self.name,
3637
"description": self.description,
38+
"details": self.details,
3739
"expected_behavior": self.expected_behavior,
3840
"required_result": self.required_result,
3941
"result": self.result
@@ -47,3 +49,6 @@ def to_dict(self):
4749
test_dict["optional_recommendations"] = self.optional_recommendations
4850

4951
return test_dict
52+
53+
def __post_init__(self):
54+
self.details = self.details.replace("\n", "", 1)

framework/python/src/test_orc/test_orchestrator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,14 +611,18 @@ def _run_test_module(self, module):
611611
# Convert dict from json into TestCase object
612612
test_case = TestCase(name=test_result["name"],
613613
result=test_result["result"],
614-
description=test_result["description"])
614+
description=test_result["description"]
615+
)
615616

616617
# Add steps to resolve if test is non-compliant
617618
if (test_case.result == TestResult.NON_COMPLIANT
618619
and "recommendations" in test_result):
619620
test_case.recommendations = test_result["recommendations"]
620621
else:
621622
test_case.recommendations = []
623+
# Add details to the test case if presented
624+
if "details" in test_result:
625+
test_case.details = test_result["details"]
622626

623627
self.get_session().add_test_result(test_case)
624628

modules/ui/src/app/components/list-layout/list-layout.component.html

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,23 @@
3838
</mat-toolbar-row>
3939
<mat-toolbar-row>
4040
<div class="search-field">
41-
<input (input)="updateQuery($event)" placeholder="Search" />
42-
<mat-icon>search</mat-icon>
43-
</div></mat-toolbar-row
44-
>
41+
<input
42+
[value]="searchText()"
43+
(input)="updateQuery($event)"
44+
placeholder="Search" />
45+
<button
46+
*ngIf="searchText().length > 2; else searchIcon"
47+
(click)="clearSearch()"
48+
class="clear-search-button"
49+
mat-icon-button
50+
aria-label="Clear search">
51+
<mat-icon>close</mat-icon>
52+
</button>
53+
<ng-template #searchIcon>
54+
<mat-icon>search</mat-icon>
55+
</ng-template>
56+
</div>
57+
</mat-toolbar-row>
4558
</mat-toolbar>
4659
<div class="entity-list-container">
4760
<section class="entity-list">

modules/ui/src/app/components/list-layout/list-layout.component.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@
112112
}
113113
}
114114

115+
.clear-search-button {
116+
margin-right: 10px;
117+
}
118+
115119
::ng-deep .using-mouse .search-field input:focus {
116120
outline: none;
117121
}

modules/ui/src/app/components/list-layout/list-layout.component.spec.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { ComponentFixture, TestBed } from '@angular/core/testing';
16+
import {
17+
ComponentFixture,
18+
fakeAsync,
19+
TestBed,
20+
tick,
21+
} from '@angular/core/testing';
1722
import { ListLayoutComponent } from './list-layout.component';
1823
import { MatSidenavModule } from '@angular/material/sidenav';
1924
import { MatToolbarModule } from '@angular/material/toolbar';
@@ -22,6 +27,7 @@ import { MatButtonModule } from '@angular/material/button';
2227
import { ListItemComponent } from '../list-item/list-item.component';
2328
import { Component } from '@angular/core';
2429
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
30+
import { By } from '@angular/platform-browser';
2531

2632
interface Entity {
2733
id: number;
@@ -63,6 +69,7 @@ describe('ListLayoutComponent', () => {
6369
let component: HostComponent;
6470
let fixture: ComponentFixture<HostComponent>;
6571
let compiled: HTMLElement;
72+
let listLayoutComponentInstance: ListLayoutComponent<Entity>;
6673

6774
beforeEach(async () => {
6875
await TestBed.configureTestingModule({
@@ -79,6 +86,13 @@ describe('ListLayoutComponent', () => {
7986
fixture = TestBed.createComponent(HostComponent);
8087
component = fixture.componentInstance;
8188
compiled = fixture.nativeElement as HTMLElement;
89+
90+
const listLayoutDebugElement = fixture.debugElement.query(
91+
By.directive(ListLayoutComponent)
92+
);
93+
listLayoutComponentInstance =
94+
listLayoutDebugElement.componentInstance as ListLayoutComponent<Entity>;
95+
8296
fixture.detectChanges();
8397
});
8498

@@ -146,5 +160,46 @@ describe('ListLayoutComponent', () => {
146160

147161
expect(listItemComponent.length).toEqual(2);
148162
});
163+
164+
it('clearSearch() should reset searchText to an empty string and clear input', fakeAsync(() => {
165+
const searchInputElement = compiled.querySelector(
166+
'.search-field input'
167+
) as HTMLInputElement;
168+
expect(searchInputElement).toBeTruthy();
169+
170+
searchInputElement.value = 'testing';
171+
searchInputElement.dispatchEvent(new Event('input'));
172+
fixture.detectChanges();
173+
tick();
174+
175+
// Verify ListLayoutComponent's internal searchText is updated
176+
expect(listLayoutComponentInstance.searchText()).toBe('testing');
177+
178+
const buttonClearSearch = compiled.querySelector(
179+
'.clear-search-button'
180+
) as HTMLButtonElement;
181+
expect(buttonClearSearch).toBeTruthy();
182+
183+
buttonClearSearch.click();
184+
fixture.detectChanges();
185+
tick();
186+
187+
// Verify ListLayoutComponent's internal searchText is cleared
188+
expect(listLayoutComponentInstance.searchText()).toBe('');
189+
190+
// Verify the input field in the DOM is also cleared
191+
expect(searchInputElement.value).toBe('');
192+
193+
// Verify the clear button is gone and search icon is back
194+
const clearButtonAfterClear = compiled.querySelector(
195+
'.clear-search-button'
196+
);
197+
expect(clearButtonAfterClear).toBeNull();
198+
const searchIcon = compiled.querySelector(
199+
'.search-field mat-icon:not(button mat-icon)'
200+
);
201+
expect(searchIcon).toBeTruthy();
202+
expect(searchIcon?.textContent?.trim()).toBe('search');
203+
}));
149204
});
150205
});

modules/ui/src/app/components/list-layout/list-layout.component.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ export class ListLayoutComponent<T extends object> {
8282
return this.actions();
8383
};
8484

85+
clearSearch(): void {
86+
this.searchText.set('');
87+
}
88+
8589
updateQuery(e: Event) {
8690
const input = e.target as HTMLInputElement;
8791
const value = input.value;

modules/ui/src/app/model/testrun-status.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export interface IResult {
5353
description: string;
5454
result: string;
5555
recommendations?: string[];
56+
details?: string;
5657
required_result: RequiredResult;
5758
}
5859

modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ describe('DeviceQualificationFromComponent', () => {
448448
autoFocus: true,
449449
hasBackdrop: true,
450450
disableClose: true,
451-
panelClass: ['simple-dialog', 'close-device'],
451+
panelClass: ['simple-dialog', 'discard-dialog'],
452452
});
453453

454454
openSpy.calls.reset();

modules/ui/src/app/pages/devices/components/device-qualification-from/device-qualification-from.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ export class DeviceQualificationFromComponent implements OnInit, AfterViewInit {
476476
autoFocus: true,
477477
hasBackdrop: true,
478478
disableClose: true,
479-
panelClass: ['simple-dialog', 'close-device'],
479+
panelClass: ['simple-dialog', 'discard-dialog'],
480480
});
481481

482482
return dialogRef?.beforeClosed();

modules/ui/src/app/pages/general-settings/general-settings.component.html

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,13 @@
9595
"></ng-container>
9696
<mat-divider></mat-divider>
9797
</section>
98-
<mat-error
99-
*ngIf="vm.isSubmitting && isFormError"
100-
class="error-message-container">
101-
<span>Both interfaces must have different values</span>
102-
</mat-error>
98+
<div class="section-item error-message-section">
99+
<mat-error
100+
*ngIf="vm.isSubmitting && isFormError"
101+
class="error-message-container">
102+
<span>Both interfaces must have different values</span>
103+
</mat-error>
104+
</div>
103105
<div class="setting-drawer-footer">
104106
<button
105107
mat-flat-button

modules/ui/src/app/pages/general-settings/general-settings.component.scss

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,15 @@
3737
}
3838
}
3939

40+
.section-item.error-message-section {
41+
min-height: 20px;
42+
padding-bottom: 0;
43+
}
44+
4045
.error-message-container {
41-
display: block;
42-
margin-top: auto;
43-
padding-bottom: 8px;
44-
text-align: center;
46+
display: flex;
47+
align-items: flex-end;
48+
grid-column: 2 / 4;
4549
}
4650

4751
.message {

modules/ui/src/app/pages/general-settings/general-settings.store.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,20 @@ export class GeneralSettingsStore extends ComponentStore<SettingsComponentState>
151151
config: SystemConfig;
152152
}>(trigger$ => {
153153
return trigger$.pipe(
154-
exhaustMap(trigger => {
155-
return this.testRunService.createSystemConfig(trigger.config).pipe(
156-
tap(() => {
157-
this.store.dispatch(
158-
AppActions.fetchSystemConfigSuccess({
159-
systemConfig: trigger.config,
160-
})
161-
);
162-
trigger.onSystemConfigUpdate();
163-
})
164-
);
154+
withLatestFrom(this.systemConfig$),
155+
exhaustMap(([trigger, currentConfig]) => {
156+
return this.testRunService
157+
.createSystemConfig({ ...currentConfig, ...trigger.config })
158+
.pipe(
159+
tap(() => {
160+
this.store.dispatch(
161+
AppActions.fetchSystemConfigSuccess({
162+
systemConfig: { ...currentConfig, ...trigger.config },
163+
})
164+
);
165+
trigger.onSystemConfigUpdate();
166+
})
167+
);
165168
})
166169
);
167170
});

modules/ui/src/app/pages/testrun/components/test-result-dialog/test-result-dialog.component.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
<ul class="info-steps-list">
3232
<li *ngFor="let point of data.testResult.recommendations">{{ point }}</li>
3333
</ul>
34+
<div *ngIf="data.testResult.details" class="details-info">
35+
<p class="info-label">Details</p>
36+
<p class="info-details">{{ data.testResult.details }}</p>
37+
<p></p>
38+
</div>
3439
</section>
3540
</mat-dialog-content>
3641
<mat-dialog-actions align="end" class="actions-container">

modules/ui/src/app/pages/testrun/components/test-result-dialog/test-result-dialog.component.scss

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,19 @@ mat-dialog-content.content-container {
8686
padding: 32px 48px;
8787
}
8888

89-
.info-steps-list {
89+
.info-steps-list,
90+
.info-details {
9091
margin: 0;
9192
padding-left: 24px;
9293
font-size: 16px;
9394
line-height: 24px;
9495
letter-spacing: 0;
9596
}
9697

98+
.details-info {
99+
margin-top: 16px;
100+
}
101+
97102
mat-dialog-actions.actions-container {
98103
padding: 18px 36px 26px;
99104
}

0 commit comments

Comments
 (0)