Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 2 additions & 35 deletions docs/dev/mockoon.json

Large diffs are not rendered by default.

35 changes: 32 additions & 3 deletions framework/python/src/core/testrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
DEVICE_TEST_PACK_KEY = 'test_pack'
DEVICE_ADDITIONAL_INFO_KEY = 'additional_info'
DEVICE_REPORT_NAME_FORMAT = '{mac_addr}_{timestamp}'
DEVICE_QUESTIONS_FILE_NAME = 'device_profile.json'

MAX_DEVICE_REPORTS_KEY = 'max_device_reports'

Expand Down Expand Up @@ -283,9 +284,37 @@ def _load_devices(self, device_dir):
device.additional_info = device_config_json.get(
DEVICE_ADDITIONAL_INFO_KEY)

if None in [device.type, device.technology, device.test_pack]:
LOGGER.warning(
'Device is outdated and requires further configuration')
format_file_path = os.path.join(self.get_root_dir(),
RESOURCE_DEVICES_DIR,
DEVICE_QUESTIONS_FILE_NAME)
with open(format_file_path, 'r', encoding='utf-8') as f:
format_data = json.load(f)

required_questions = [
item['question'] for item in format_data
if item.get('validation', {}).get('required') is True
]

current_answers = \
device.additional_info if device.additional_info else []
answered_questions = \
[entry.get('question') for entry in current_answers]

missing_answers = [q for q in required_questions if
q not in answered_questions]

if (None in [device.type, device.technology, device.test_pack] or
len(missing_answers) > 0):
if missing_answers:
LOGGER.warning(
f'Device : {device}'
)
LOGGER.warning(
f'Device is missing required additional info: {missing_answers}'
)
else:
LOGGER.warning(
'Device is outdated and requires further configuration')
device.status = 'Invalid'

if not device.get_reports():
Expand Down
2 changes: 1 addition & 1 deletion make/DEBIAN/control
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Package: Testrun
Version: 2.3.4-beta.4
Version: 2.4.0-beta.3
Architecture: amd64
Maintainer: Google <ssm-orcas@google.com>
Homepage: https://github.com/google/testrun
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ <h2 tabindex="-1">Welcome to Testrun!</h2>
>
to share you thoughts
</li>
<li>Risk Profile was lastly updated in 2026 V2.4.0</li>
</ul>
</section>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ type DialogData = {

@Component({
selector: 'app-consent-dialog',

imports: [
MatDialogModule,
MatButtonModule,
Expand Down
1 change: 0 additions & 1 deletion modules/ui/src/app/components/version/version.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export const INSTALLED_VERSION = 'INSTALLED_VERSION';
declare const gtag: Function;
@Component({
selector: 'app-version',

imports: [CommonModule, MatButtonModule, MatDialogModule],
templateUrl: './version.component.html',
styleUrls: ['./version.component.scss'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<form [formGroup]="profileForm" class="profile-form">
<form
[formGroup]="profileForm"
class="profile-form"
[class.profile-form-outdated]="
selectedProfile?.status === ProfileStatus.EXPIRED
">
<div class="field-container">
<label class="field-label name-field-label" for="name-field"
>Profile name *</label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
}
}

.profile-form-outdated .field-container {
opacity: 0.5;
pointer-events: none;
}

.profile-form-field ::ng-deep .mat-mdc-form-field-textarea-control {
display: inherit;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ProfileFormComponent } from './profile-form.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {
COPY_PROFILE_MOCK,
EXPIRED_PROFILE_MOCK,
NEW_PROFILE_MOCK,
NEW_PROFILE_MOCK_DRAFT,
OUTDATED_DRAFT_PROFILE_MOCK,
Expand All @@ -35,6 +36,7 @@ import { of } from 'rxjs';
import { MatDialogRef } from '@angular/material/dialog';
import { SimpleDialogComponent } from '../../../components/simple-dialog/simple-dialog.component';
import SpyObj = jasmine.SpyObj;
import { By } from '@angular/platform-browser';

describe('ProfileFormComponent', () => {
let component: ProfileFormComponent;
Expand Down Expand Up @@ -291,6 +293,21 @@ describe('ProfileFormComponent', () => {
});
});

describe('with expired profile', () => {
beforeEach(() => {
component.selectedProfile = EXPIRED_PROFILE_MOCK;
fixture.detectChanges();
});

it('should have a form with "outdated" clsss', () => {
const form = fixture.debugElement.query(
By.css('.profile-form-outdated')
);

expect(form).toBeTruthy();
});
});

describe('with profile', () => {
beforeEach(() => {
component.selectedProfile = PROFILE_MOCK;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,12 @@ export class ProfileFormComponent implements OnInit, AfterViewInit {
return false;
}

for (const question of profile1.questions) {
for (const question of profile2.questions) {
const answer1 = question.answer;
const answer2 = profile2.questions?.find(
const answer2 = profile1.questions?.find(
question2 => question2.question === question.question
)?.answer;
if (answer1 !== undefined && answer2 !== undefined) {
if (!this.isEmptyAnswer(answer1) && !this.isEmptyAnswer(answer2)) {
if (typeof question.answer === 'string') {
if (answer1 !== answer2) {
return false;
Expand All @@ -262,13 +262,19 @@ export class ProfileFormComponent implements OnInit, AfterViewInit {
)
return false;
}
} else {
return !!answer1 == !!answer2;
} else if (this.isEmptyAnswer(answer2) && !this.isEmptyAnswer(answer1)) {
return false;
}
}
return true;
}

private isEmptyAnswer(answer: unknown): boolean {
if (answer === undefined || answer === null || answer === '') return true;
if (Array.isArray(answer) && answer.length === 0) return true;
return false;
}

private get fieldsHasError(): boolean {
return this.profileFormat.some((field, index) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,14 @@
role="button"
tabindex="0"
#tooltip="matTooltip"
matTooltip="{{
profile.status === ProfileStatus.EXPIRED
? EXPIRED_TOOLTIP
: profile.status
}}"
matTooltip="{{ profile.status }}"
[attr.aria-label]="getProfileItemLabel(profile)"
(click)="profileClicked.emit(profile)"
(keydown.enter)="enterProfileItem(profile)"
(keydown.space)="enterProfileItem(profile)">
<span
class="profile-item-icon-container"
[attr.aria-label]="
profile.status === ProfileStatus.EXPIRED
? EXPIRED_TOOLTIP
: profile.status
">
[attr.aria-label]="profile.status">
@if (profile.status === ProfileStatus.VALID) {
<mat-icon class="profile-item-icon" fontSet="material-symbols-outlined">
check_circle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,7 @@ $profile-item-container-gap: 8px;
}
}

:host:has(.profile-item-container-expired) {
cursor: not-allowed;
}

.profile-item-container-expired {
pointer-events: none;
opacity: 0.5;
.profile-item-info {
.profile-item-icon,
.profile-item-name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
ComponentFixture,
fakeAsync,
TestBed,
tick,
} from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { ProfileItemComponent } from './profile-item.component';
import {
EXPIRED_PROFILE_MOCK,
PROFILE_MOCK,
} from '../../../mocks/profile.mock';
import { PROFILE_MOCK } from '../../../mocks/profile.mock';
import { TestRunService } from '../../../services/test-run.service';
import { LiveAnnouncer } from '@angular/cdk/a11y';

Expand Down Expand Up @@ -80,18 +72,6 @@ describe('ProfileItemComponent', () => {
expect(profileClickedSpy).toHaveBeenCalledWith(PROFILE_MOCK);
});

it('should change tooltip on focusout', fakeAsync(() => {
component.profile = EXPIRED_PROFILE_MOCK;
fixture.detectChanges();

fixture.nativeElement.dispatchEvent(new Event('focusout'));
tick();

expect(component.tooltip().message).toEqual(
'Expired. Please, create a new Risk profile.'
);
}));

it('#getRiskClass should call getRiskClass on testRunService', () => {
const MOCK_RISK = 'mock value';
component.getRiskClass(MOCK_RISK);
Expand All @@ -105,22 +85,4 @@ describe('ProfileItemComponent', () => {

expect(profileClickedSpy).toHaveBeenCalled();
});

describe('with Expired profile', () => {
beforeEach(() => {
component.enterProfileItem(EXPIRED_PROFILE_MOCK);
});

it('should change tooltip on enterProfileItem', () => {
expect(component.tooltip().message).toEqual(
'This risk profile is outdated. Please create a new risk profile.'
);
});

it('should announce', () => {
expect(mockLiveAnnouncer.announce).toHaveBeenCalledWith(
'This risk profile is outdated. Please create a new risk profile.'
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ import {
ChangeDetectionStrategy,
Component,
EventEmitter,
HostListener,
Input,
Output,
viewChild,
inject,
} from '@angular/core';
import {
Expand All @@ -33,7 +31,6 @@ import { MatButtonModule } from '@angular/material/button';
import { CommonModule, DatePipe } from '@angular/common';
import { TestRunService } from '../../../services/test-run.service';
import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip';
import { LiveAnnouncer } from '@angular/cdk/a11y';

@Component({
selector: 'app-profile-item',
Expand All @@ -46,40 +43,18 @@ import { LiveAnnouncer } from '@angular/cdk/a11y';
})
export class ProfileItemComponent {
private readonly testRunService = inject(TestRunService);
private liveAnnouncer = inject(LiveAnnouncer);
private datePipe = inject(DatePipe);

public readonly ProfileStatus = ProfileStatus;
public readonly EXPIRED_TOOLTIP =
'Expired. Please, create a new Risk profile.';
@Input() profile!: Profile;
@Output() profileClicked = new EventEmitter<Profile>();

readonly tooltip = viewChild.required<MatTooltip>('tooltip');

@HostListener('focusout')
outEvent(): void {
if (this.profile.status === ProfileStatus.EXPIRED) {
this.tooltip().message = this.EXPIRED_TOOLTIP;
}
}

public getRiskClass(riskResult: string): RiskResultClassName {
return this.testRunService.getRiskClass(riskResult);
}

public async enterProfileItem(profile: Profile) {
if (profile.status === ProfileStatus.EXPIRED) {
const tooltip = this.tooltip();
tooltip.message =
'This risk profile is outdated. Please create a new risk profile.';
tooltip.show();
await this.liveAnnouncer.announce(
'This risk profile is outdated. Please create a new risk profile.'
);
} else {
this.profileClicked.emit(profile);
}
this.profileClicked.emit(profile);
}

getProfileItemLabel(profile: Profile) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,7 @@ export class RiskAssessmentComponent
}

async profileClicked(profile: Profile | null = null) {
if (profile === null || profile.status !== ProfileStatus.EXPIRED) {
await this.openForm(profile);
}
await this.openForm(profile);
}

async openForm(profile: Profile | null = null) {
Expand Down Expand Up @@ -289,10 +287,6 @@ export class RiskAssessmentComponent
if (profile.status === ProfileStatus.COPY) {
return [];
}
// expired profiles can only be removed
if (profile.status === ProfileStatus.EXPIRED) {
return [{ action: ProfileAction.Delete, icon: 'delete' }];
}
return actions;
};
}
Expand Down
Loading
Loading