Merge pull request #11254 from jwangyangls/nightly-case-3

[Fix]   Fix issue 2.0
This commit is contained in:
jwangyangls 2020-03-26 12:14:37 +08:00 committed by GitHub
commit 96572c3c86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 102 additions and 49 deletions

View File

@ -91,7 +91,7 @@
[(clrDgSelected)]="selectedRow"> [(clrDgSelected)]="selectedRow">
<clr-dg-action-bar> <clr-dg-action-bar>
<button [clrLoading]="scanBtnState" type="button" class="btn btn-secondary scan-btn" <button [clrLoading]="scanBtnState" type="button" class="btn btn-secondary scan-btn"
[disabled]="!(canScanNow() && selectedRowHasVul() && selectedRow.length==1 && hasEnabledScanner && hasScanImagePermission )" (click)="scanNow()"> [disabled]="!(canScanNow() && selectedRowHasVul() && hasEnabledScanner && hasScanImagePermission )" (click)="scanNow()">
<clr-icon shape="shield-check" size="16"></clr-icon>&nbsp;{{'VULNERABILITY.SCAN_NOW' | translate}} <clr-icon shape="shield-check" size="16"></clr-icon>&nbsp;{{'VULNERABILITY.SCAN_NOW' | translate}}
</button> </button>
@ -110,7 +110,7 @@
(click)="addLabels()"> (click)="addLabels()">
{{'REPOSITORY.ADD_LABELS' | translate}} {{'REPOSITORY.ADD_LABELS' | translate}}
</button> </button>
<clr-dropdown-menu> <clr-dropdown-menu [hidden]="!selectedRow.length">
<div class="filter-grid"> <div class="filter-grid">
<label <label
class="dropdown-header">{{'REPOSITORY.ADD_LABEL_TO_IMAGE' | translate}}</label> class="dropdown-header">{{'REPOSITORY.ADD_LABEL_TO_IMAGE' | translate}}</label>
@ -208,17 +208,17 @@
<tr> <tr>
<th class="left tag-header-color"> <th class="left tag-header-color">
{{'REPOSITORY.TAGS_COUNT' | translate | uppercase}}</th> {{'REPOSITORY.TAGS_COUNT' | translate | uppercase}}</th>
<th class="left tag-header-color">
{{'REPOSITORY.PUSH_TIME' | translate | uppercase}}</th>
<th class="left tag-header-color"> <th class="left tag-header-color">
{{'REPOSITORY.PULL_TIME' | translate | uppercase}}</th> {{'REPOSITORY.PULL_TIME' | translate | uppercase}}</th>
<th class="left tag-header-color">
{{'REPOSITORY.PUSH_TIME' | translate | uppercase}}</th>
</tr> </tr>
</thead> </thead>
<tbody class="tag-tbody"> <tbody class="tag-tbody">
<tr class="tag-tr" *ngFor="let tag of artifact.tags"> <tr class="tag-tr" *ngFor="let tag of artifact.tags">
<td class="left tag-body-color">{{tag.name}}</td> <td class="left tag-body-color">{{tag.name}}</td>
<td class="left tag-body-color">{{tag.pull_time === availableTime ? '':(tag.pull_time | date: 'short')}}</td>
<td class="left tag-body-color">{{tag.push_time | date: 'short'}}</td> <td class="left tag-body-color">{{tag.push_time | date: 'short'}}</td>
<td class="left tag-body-color">{{tag.pull_time | date: 'short'}}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -162,6 +162,10 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
openSelectFilterPiece = false; openSelectFilterPiece = false;
// could Pagination filter // could Pagination filter
filters: string[]; filters: string[];
scanFiinishArtifactLength: number = 0;
onScanArtifactsLength: number = 0;
constructor( constructor(
private errorHandlerService: ErrorHandler, private errorHandlerService: ErrorHandler,
private userPermissionService: UserPermissionService, private userPermissionService: UserPermissionService,
@ -731,6 +735,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
forkJoin(...this.deleteArtifactobservableLists).subscribe((deleteResult) => { forkJoin(...this.deleteArtifactobservableLists).subscribe((deleteResult) => {
let deleteSuccessList = []; let deleteSuccessList = [];
let deleteErrorList = []; let deleteErrorList = [];
this.deleteArtifactobservableLists = [];
deleteResult.forEach(result => { deleteResult.forEach(result => {
if (!result) { if (!result) {
// delete success // delete success
@ -739,9 +744,9 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
deleteErrorList.push(result); deleteErrorList.push(result);
} }
}); });
this.selectedRow = [];
if (deleteSuccessList.length === deleteResult.length) { if (deleteSuccessList.length === deleteResult.length) {
// all is success // all is success
this.selectedRow = [];
let st: ClrDatagridStateInterface = { page: {from: 0, to: this.pageSize - 1, size: this.pageSize} }; let st: ClrDatagridStateInterface = { page: {from: 0, to: this.pageSize - 1, size: this.pageSize} };
this.clrLoad(st); this.clrLoad(st);
} else if (deleteErrorList.length === deleteResult.length) { } else if (deleteErrorList.length === deleteResult.length) {
@ -752,7 +757,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
// some artifact delete success but it has error delete things // some artifact delete success but it has error delete things
this.errorHandlerService.error(deleteErrorList[deleteErrorList.length - 1].error); this.errorHandlerService.error(deleteErrorList[deleteErrorList.length - 1].error);
// if delete one success refresh list // if delete one success refresh list
this.selectedRow = [];
let st: ClrDatagridStateInterface = { page: {from: 0, to: this.pageSize - 1, size: this.pageSize} }; let st: ClrDatagridStateInterface = { page: {from: 0, to: this.pageSize - 1, size: this.pageSize} };
this.clrLoad(st); this.clrLoad(st);
} }
@ -871,10 +875,16 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
} }
// Trigger scan // Trigger scan
scanNow(): void { scanNow(): void {
if (this.selectedRow && this.selectedRow.length === 1) { if (!this.selectedRow.length) {
this.onSendingScanCommand = true; return;
this.channel.publishScanEvent(this.repoName + "/" + this.selectedRow[0].digest);
} }
this.scanFiinishArtifactLength = 0;
this.onScanArtifactsLength = this.selectedRow.length;
this.onSendingScanCommand = true;
this.selectedRow.forEach((data: any) => {
let digest = data.digest;
this.channel.publishScanEvent(this.repoName + "/" + digest);
});
} }
selectedRowHasVul(): boolean { selectedRowHasVul(): boolean {
return !!(this.selectedRow return !!(this.selectedRow
@ -886,7 +896,11 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
return !!(artifact && artifact.addition_links && artifact.addition_links[ADDITIONS.VULNERABILITIES]); return !!(artifact && artifact.addition_links && artifact.addition_links[ADDITIONS.VULNERABILITIES]);
} }
submitFinish(e: boolean) { submitFinish(e: boolean) {
this.onSendingScanCommand = e; this.scanFiinishArtifactLength += 1;
// all selected scan action has start
if (this.scanFiinishArtifactLength === this.onScanArtifactsLength) {
this.onSendingScanCommand = e;
}
} }
// pull command // pull command
onCpError($event: any): void { onCpError($event: any): void {

View File

@ -91,7 +91,10 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
this.hasShowLoading = true; this.hasShowLoading = true;
} }
this.additionsService.getDetailByLink(this.vulnerabilitiesLink.href) this.additionsService.getDetailByLink(this.vulnerabilitiesLink.href)
.pipe(finalize(() => this.loading = false)) .pipe(finalize(() => {
this.loading = false;
this.hasShowLoading = false;
}))
.subscribe( .subscribe(
res => { res => {
this.scan_overview = res; this.scan_overview = res;

View File

@ -22,7 +22,7 @@
<artifact-common-properties [artifactDetails]="artifact"></artifact-common-properties> <artifact-common-properties [artifactDetails]="artifact"></artifact-common-properties>
<!-- tags --> <!-- tags -->
<artifact-tag [artifactDetails]="artifact" [projectName]="projectName" [repositoryName]="repositoryName" <artifact-tag [artifactDetails]="artifact" [projectName]="projectName" [projectId]="projectId" [repositoryName]="repositoryName"
(refreshArtifact)="refreshArtifact()"></artifact-tag> (refreshArtifact)="refreshArtifact()"></artifact-tag>
<!-- Additions --> <!-- Additions -->

View File

@ -4,7 +4,7 @@
<button type="button" class="btn btn-secondary" (click)="addTag()"> <button type="button" class="btn btn-secondary" (click)="addTag()">
<clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'TAG.ADD_TAG' | translate}} <clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'TAG.ADD_TAG' | translate}}
</button> </button>
<button type="button" class="btn btn-secondary" [disabled]="!(selectedRow.length>=1)" (click)="removeTag()"> <button type="button" class="btn btn-secondary" [disabled]="!(selectedRow.length>=1&& !hasImmutableOnTag() && hasDeleteTagPermission)" (click)="removeTag()">
<clr-icon shape="trash" size="16"></clr-icon>&nbsp;{{'TAG.REMOVE_TAG' | translate}} <clr-icon shape="trash" size="16"></clr-icon>&nbsp;{{'TAG.REMOVE_TAG' | translate}}
</button> </button>
<form #tagForm="ngForm" [hidden]="!newTagformShow" class="label-form stack-block-label"> <form #tagForm="ngForm" [hidden]="!newTagformShow" class="label-form stack-block-label">
@ -46,7 +46,7 @@
<span *ngIf="tag.immutable" class="label label-info ml-8">{{'REPOSITORY.IMMUTABLE' | translate}}</span> <span *ngIf="tag.immutable" class="label label-info ml-8">{{'REPOSITORY.IMMUTABLE' | translate}}</span>
</div> </div>
</clr-dg-cell> </clr-dg-cell>
<clr-dg-cell [ngSwitch]="tag.signature != null"> <clr-dg-cell [ngSwitch]="tag.signed">
<div class="cell"> <div class="cell">
<clr-icon shape="check-circle" *ngSwitchCase="true" size="20" class="color-green"></clr-icon> <clr-icon shape="check-circle" *ngSwitchCase="true" size="20" class="color-green"></clr-icon>
<clr-icon shape="times-circle" *ngSwitchCase="false" size="16" class="color-red"></clr-icon> <clr-icon shape="times-circle" *ngSwitchCase="false" size="16" class="color-red"></clr-icon>

View File

@ -10,6 +10,7 @@ import { ErrorHandler } from "../../../../../lib/utils/error-handler";
import { ArtifactService } from '../../../../../../ng-swagger-gen/services/artifact.service'; import { ArtifactService } from '../../../../../../ng-swagger-gen/services/artifact.service';
import { OperationService } from "../../../../../lib/components/operation/operation.service"; import { OperationService } from "../../../../../lib/components/operation/operation.service";
import { CURRENT_BASE_HREF } from "../../../../../lib/utils/utils"; import { CURRENT_BASE_HREF } from "../../../../../lib/utils/utils";
import { USERSTATICPERMISSION, UserPermissionService, UserPermissionDefaultService } from '../../../../../lib/services';
describe('ArtifactTagComponent', () => { describe('ArtifactTagComponent', () => {
@ -25,6 +26,11 @@ describe('ArtifactTagComponent', () => {
const config: IServiceConfig = { const config: IServiceConfig = {
repositoryBaseEndpoint: CURRENT_BASE_HREF + "/repositories/testing" repositoryBaseEndpoint: CURRENT_BASE_HREF + "/repositories/testing"
}; };
let userPermissionService;
const permissions = [
{ resource: USERSTATICPERMISSION.REPOSITORY_TAG.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG.VALUE.DELETE },
];
let mockHasDeleteImagePermission: boolean = true;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
@ -41,6 +47,7 @@ describe('ArtifactTagComponent', () => {
{ provide: SERVICE_CONFIG, useValue: config }, { provide: SERVICE_CONFIG, useValue: config },
{ provide: mockErrorHandler, useValue: ErrorHandler }, { provide: mockErrorHandler, useValue: ErrorHandler },
{ provide: ArtifactService, useValue: mockArtifactService }, { provide: ArtifactService, useValue: mockArtifactService },
{ provide: UserPermissionService, useClass: UserPermissionDefaultService },
{ provide: OperationService }, { provide: OperationService },
] ]
}) })
@ -50,6 +57,11 @@ describe('ArtifactTagComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ArtifactTagComponent); fixture = TestBed.createComponent(ArtifactTagComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
userPermissionService = fixture.debugElement.injector.get(UserPermissionService);
spyOn(userPermissionService, "hasProjectPermissions")
.withArgs(component.projectId, permissions)
.and.returnValue(of([
mockHasDeleteImagePermission]));
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -17,7 +17,10 @@ import { errorHandler } from "../../../../../lib/utils/shared/shared.utils";
import { ArtifactFront as Artifact } from "../artifact"; import { ArtifactFront as Artifact } from "../artifact";
import { ArtifactService } from '../../../../../../ng-swagger-gen/services/artifact.service'; import { ArtifactService } from '../../../../../../ng-swagger-gen/services/artifact.service';
import { Tag } from '../../../../../../ng-swagger-gen/models/tag'; import { Tag } from '../../../../../../ng-swagger-gen/models/tag';
import {
UserPermissionService, USERSTATICPERMISSION
} from "../../../../../lib/services";
class InitTag { class InitTag {
name = ""; name = "";
} }
@ -30,6 +33,7 @@ class InitTag {
export class ArtifactTagComponent implements OnInit { export class ArtifactTagComponent implements OnInit {
@Input() artifactDetails: Artifact; @Input() artifactDetails: Artifact;
@Input() projectName: string; @Input() projectName: string;
@Input() projectId: number;
@Input() repositoryName: string; @Input() repositoryName: string;
@Output() refreshArtifact = new EventEmitter(); @Output() refreshArtifact = new EventEmitter();
newTagName = new InitTag(); newTagName = new InitTag();
@ -43,15 +47,25 @@ export class ArtifactTagComponent implements OnInit {
availableTime = AVAILABLE_TIME; availableTime = AVAILABLE_TIME;
@ViewChild("confirmationDialog", { static: false }) @ViewChild("confirmationDialog", { static: false })
confirmationDialog: ConfirmationDialogComponent; confirmationDialog: ConfirmationDialogComponent;
hasDeleteTagPermission: boolean;
constructor( constructor(
private operationService: OperationService, private operationService: OperationService,
private artifactService: ArtifactService, private artifactService: ArtifactService,
private translateService: TranslateService, private translateService: TranslateService,
private userPermissionService: UserPermissionService,
private errorHandlerService: ErrorHandler private errorHandlerService: ErrorHandler
) { } ) { }
ngOnInit() { ngOnInit() {
this.getImagePermissionRule(this.projectId);
}
getImagePermissionRule(projectId: number): void {
const permissions = [
{ resource: USERSTATICPERMISSION.REPOSITORY_TAG.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG.VALUE.DELETE }
];
this.userPermissionService.hasProjectPermissions(this.projectId, permissions).subscribe((results: Array<boolean>) => {
this.hasDeleteTagPermission = results[0];
}, error => this.errorHandlerService.error(error));
} }
addTag() { addTag() {
@ -166,4 +180,8 @@ export class ArtifactTagComponent implements OnInit {
this.openTag = !this.openTag; this.openTag = !this.openTag;
this.newTagformShow = false; this.newTagformShow = false;
} }
hasImmutableOnTag(): boolean {
return this.selectedRow.some((artifact) => artifact.immutable);
}
} }

View File

@ -68,8 +68,9 @@ export class ListRepositoryROComponent implements OnInit, OnDestroy {
public gotoLink(projectId: number, repoName: string): void { public gotoLink(projectId: number, repoName: string): void {
this.searchTrigger.closeSearch(true); this.searchTrigger.closeSearch(true);
let projectName = repoName.split('/')[0];
let linkUrl = ['harbor', 'tags', projectId, repoName]; let repositorieName = projectName ? repoName.split(`${projectName}/`)[1] : repoName;
let linkUrl = ['harbor', 'projects', projectId, 'repositories', repositorieName ];
this.router.navigate(linkUrl); this.router.navigate(linkUrl);
} }

View File

@ -693,7 +693,7 @@
"ADD_LABEL_TO_IMAGE": "Add labels to this image", "ADD_LABEL_TO_IMAGE": "Add labels to this image",
"FILTER_BY_LABEL": "Filter images by label", "FILTER_BY_LABEL": "Filter images by label",
"FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label", "FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label",
"ADD_LABELS": "Add labels", "ADD_LABELS": "Add Labels",
"RETAG": "Copy", "RETAG": "Copy",
"ACTION": "ACTION", "ACTION": "ACTION",
"DEPLOY": "DEPLOY", "DEPLOY": "DEPLOY",

View File

@ -694,7 +694,7 @@
"ADD_LABEL_TO_IMAGE": "Add labels to this image", "ADD_LABEL_TO_IMAGE": "Add labels to this image",
"FILTER_BY_LABEL": "Filter images by label", "FILTER_BY_LABEL": "Filter images by label",
"FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label", "FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label",
"ADD_LABELS": "Add labels", "ADD_LABELS": "Add Labels",
"RETAG": "Copy", "RETAG": "Copy",
"ACTION": "ACTION", "ACTION": "ACTION",
"DEPLOY": "DEPLOY", "DEPLOY": "DEPLOY",

View File

@ -680,7 +680,7 @@
"ADD_LABEL_TO_IMAGE": "Add labels to this image", "ADD_LABEL_TO_IMAGE": "Add labels to this image",
"FILTER_BY_LABEL": "Filter images by label", "FILTER_BY_LABEL": "Filter images by label",
"FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label", "FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label",
"ADD_LABELS": "Add labels", "ADD_LABELS": "Add Labels",
"RETAG": "Copy", "RETAG": "Copy",
"ACTION": "ACTION", "ACTION": "ACTION",
"DEPLOY": "DEPLOY", "DEPLOY": "DEPLOY",

View File

@ -693,7 +693,7 @@
"ADD_LABEL_TO_IMAGE": "Adicionar labels a essa imagem", "ADD_LABEL_TO_IMAGE": "Adicionar labels a essa imagem",
"FILTER_BY_LABEL": "Filtrar imagens por label", "FILTER_BY_LABEL": "Filtrar imagens por label",
"FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label", "FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label",
"ADD_LABELS": "Adicionar labels", "ADD_LABELS": "Adicionar Labels",
"RETAG": "Copy", "RETAG": "Copy",
"ACTION": "AÇÃO", "ACTION": "AÇÃO",
"DEPLOY": "DEPLOY", "DEPLOY": "DEPLOY",

View File

@ -2,29 +2,33 @@
<div class="normal-wrapper"> <div class="normal-wrapper">
<span [style.width]="buttonMarginLeft" class="font-style">{{ labelCurrent | translate }}</span> <span [style.width]="buttonMarginLeft" class="font-style">{{ labelCurrent | translate }}</span>
<span>{{(originScheduleType ? 'SCHEDULE.'+ originScheduleType.toUpperCase(): "") | translate}}</span> <span>{{(originScheduleType ? 'SCHEDULE.'+ originScheduleType.toUpperCase(): "") | translate}}</span>
<a [hidden]="originScheduleType!==SCHEDULE_TYPE.HOURLY" href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right"> <a [hidden]="originScheduleType!==SCHEDULE_TYPE.HOURLY" href="javascript:void(0)" role="tooltip"
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon> aria-haspopup="true" class="tooltip tooltip-top-right">
<span class="tooltip-content">{{'CONFIG.TOOLTIP.HOURLY_CRON' | translate}}</span> <clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.HOURLY_CRON' | translate}}</span>
</a> </a>
<a [hidden]="originScheduleType!==SCHEDULE_TYPE.WEEKLY" href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right"> <a [hidden]="originScheduleType!==SCHEDULE_TYPE.WEEKLY" href="javascript:void(0)" role="tooltip"
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon> aria-haspopup="true" class="tooltip tooltip-top-right">
<span class="tooltip-content">{{'CONFIG.TOOLTIP.WEEKLY_CRON' | translate}}</span> <clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.WEEKLY_CRON' | translate}}</span>
</a> </a>
<a [hidden]="originScheduleType!==SCHEDULE_TYPE.DAILY" href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right"> <a [hidden]="originScheduleType!==SCHEDULE_TYPE.DAILY" href="javascript:void(0)" role="tooltip" aria-haspopup="true"
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon> class="tooltip tooltip-top-right">
<span class="tooltip-content">{{'CONFIG.TOOLTIP.DAILY_CRON' | translate}}</span> <clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.DAILY_CRON' | translate}}</span>
</a> </a>
<span [hidden]="originScheduleType!==SCHEDULE_TYPE.CUSTOM">{{ "SCHEDULE.CRON" | translate }} :</span> <span [hidden]="originScheduleType!==SCHEDULE_TYPE.CUSTOM">{{ "SCHEDULE.CRON" | translate }} :</span>
<span [hidden]="originScheduleType!==SCHEDULE_TYPE.CUSTOM">{{ oriCron }}</span> <span [hidden]="originScheduleType!==SCHEDULE_TYPE.CUSTOM">{{ oriCron }}</span>
</div> </div>
<button [style.margin-left]="buttonMarginLeft" [disabled]="disabled" class="btn btn-primary " (click)="editSchedule()" id="editSchedule"> <button [style.margin-left]="buttonMarginLeft" [disabled]="disabled" class="btn btn-primary " (click)="editSchedule()"
id="editSchedule">
{{ "BUTTON.EDIT" | translate }} {{ "BUTTON.EDIT" | translate }}
</button> </button>
</div> </div>
<div class="setting-wrapper flex-layout" *ngIf="isEditMode"> <div class="setting-wrapper flex-layout" *ngIf="isEditMode">
<span [style.width]="buttonMarginLeft" class="font-style">{{ labelEdit | translate }}</span> <span [style.width]="buttonMarginLeft" class="font-style">{{ labelEdit | translate }}</span>
<div class="select select-schedule clr-select-wrapper"> <div class="select select-schedule clr-select-wrapper">
<select name="selectPolicy" id="selectPolicy" [(ngModel)]="scheduleType"> <select name="selectPolicy" id="selectPolicy" [(ngModel)]="scheduleType">
<option [value]="SCHEDULE_TYPE.NONE">{{'SCHEDULE.NONE' | translate}}</option> <option [value]="SCHEDULE_TYPE.NONE">{{'SCHEDULE.NONE' | translate}}</option>
<option [value]="SCHEDULE_TYPE.HOURLY">{{'SCHEDULE.HOURLY' | translate}}</option> <option [value]="SCHEDULE_TYPE.HOURLY">{{'SCHEDULE.HOURLY' | translate}}</option>
<option [value]="SCHEDULE_TYPE.DAILY">{{'SCHEDULE.DAILY' | translate}}</option> <option [value]="SCHEDULE_TYPE.DAILY">{{'SCHEDULE.DAILY' | translate}}</option>
@ -34,27 +38,26 @@
</div> </div>
<span class="required" [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM">{{ "SCHEDULE.CRON" | translate }}</span> <span class="required" [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM">{{ "SCHEDULE.CRON" | translate }}</span>
<div [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM" class="cron-input"> <div [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM" class="cron-input">
<label for="targetCron" aria-haspopup="true" role="tooltip" [class.invalid]="dateInvalid" class="tooltip tooltip-validation tooltip-md tooltip-top-left cron-label"> <label for="targetCron" aria-haspopup="true" role="tooltip" [class.clr-error]="dateInvalid"
<input type="text" (blur)="blurInvalid()" (input)="inputInvalid()" name=targetCron id="targetCron" #cronStringInput="ngModel" required class="clr-input form-control" class="tooltip tooltip-validation tooltip-md tooltip-top-left cron-label">
[(ngModel)]="cronString"> <input type="text" (blur)="blurInvalid()" (input)="inputInvalid()" name="targetCron" id="targetCron"
<span class="tooltip-content" *ngIf="dateInvalid"> #cronStringInput="ngModel" required class="clr-input form-control" [(ngModel)]="cronString">
{{'TOOLTIP.CRON_REQUIRED' | translate }} <clr-control-error *ngIf="dateInvalid">{{'TOOLTIP.CRON_REQUIRED' | translate}}</clr-control-error>
</span>
</label> </label>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-lg tooltip-right top-7 cron-tooltip"> <a href="javascript:void(0)" role="tooltip" aria-haspopup="true"
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon> class="tooltip tooltip-lg tooltip-right top-7 cron-tooltip">
<div class="tooltip-content table-box"> <clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<cron-tooltip></cron-tooltip> <div class="tooltip-content table-box">
</div> <cron-tooltip></cron-tooltip>
</div>
</a> </a>
</div> </div>
<div class="confirm-button"> <div class="confirm-button">
<button [style.margin-left]="buttonMarginLeft" class="btn btn-primary " <button [style.margin-left]="buttonMarginLeft" class="btn btn-primary " (click)="save()" id="config-save">
(click)="save()" id="config-save"> {{ "BUTTON.SAVE" | translate }}
{{ "BUTTON.SAVE" | translate }}
</button> </button>
<button class="btn btn-primary " (click)="isEditMode=false"> <button class="btn btn-primary " (click)="isEditMode=false">
{{ "BUTTON.CANCEL" | translate }} {{ "BUTTON.CANCEL" | translate }}
</button> </button>
</div> </div>
</div> </div>

View File

@ -47,6 +47,7 @@
.cron-tooltip { .cron-tooltip {
color: gray; color: gray;
cursor: default; cursor: default;
position: absolute;
.table-box { .table-box {
width: 20rem; width: 20rem;
} }

View File

@ -248,7 +248,7 @@ Add Labels To Tag
Retry Element Click xpath=//clr-dg-row[contains(.,'${tagName}')]//label Retry Element Click xpath=//clr-dg-row[contains(.,'${tagName}')]//label
Capture Page Screenshot add_${labelName}.png Capture Page Screenshot add_${labelName}.png
Retry Element Click xpath=//clr-dg-action-bar//clr-dropdown//span Retry Element Click xpath=//clr-dg-action-bar//clr-dropdown//span
Retry Element Click xpath=//clr-dropdown-menu//clr-dropdown//button[contains(.,'Add labels')] Retry Element Click xpath=//clr-dropdown-menu//clr-dropdown//button[contains(.,'Add Labels')]
Retry Element Click xpath=//clr-dropdown//div//label[contains(.,'${labelName}')] Retry Element Click xpath=//clr-dropdown//div//label[contains(.,'${labelName}')]
Retry Wait Until Page Contains Element xpath=//clr-dg-row//label[contains(.,'${labelName}')] Retry Wait Until Page Contains Element xpath=//clr-dg-row//label[contains(.,'${labelName}')]

View File

@ -76,6 +76,7 @@ Switch To Scanners Page
Should Display The Default Clair Scanner Should Display The Default Clair Scanner
Retry Wait Until Page Contains Element //clr-datagrid//clr-dg-row//clr-dg-cell[contains(.,'Clair')]//span[contains(.,'Default')] Retry Wait Until Page Contains Element //clr-datagrid//clr-dg-row//clr-dg-cell[contains(.,'Clair')]//span[contains(.,'Default')]
Clair Is Immutable Scanner Clair Is Immutable Scanner
Retry Element Click //clr-dg-row[contains(.,'Clair')]//clr-radio-wrapper/label Retry Element Click //clr-dg-row[contains(.,'Clair')]//clr-radio-wrapper/label
Retry Double Keywords When Error Retry Element Click ${scanner_action_xpath} Retry Wait Until Page Contains Element ${delete_scanner_action_xpath} Retry Double Keywords When Error Retry Element Click ${scanner_action_xpath} Retry Wait Until Page Contains Element ${delete_scanner_action_xpath}