Remove forced ng-check for registries component (#14302)

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
Will Sun 2021-02-25 15:35:30 +08:00 committed by GitHub
parent a4a995327b
commit 1955b57701
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 95 additions and 177 deletions

View File

@ -17,7 +17,6 @@ import {
EventEmitter, EventEmitter,
ViewChild, ViewChild,
AfterViewChecked, AfterViewChecked,
ChangeDetectorRef,
OnDestroy, OnDestroy,
OnInit OnInit
} from "@angular/core"; } from "@angular/core";
@ -78,7 +77,6 @@ export class CreateEditEndpointComponent
private endpointService: EndpointService, private endpointService: EndpointService,
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
private translateService: TranslateService, private translateService: TranslateService,
private ref: ChangeDetectorRef,
private http: HttpClient, private http: HttpClient,
private appConfigService: AppConfigService, private appConfigService: AppConfigService,
) {} ) {}
@ -201,22 +199,6 @@ export class CreateEditEndpointComponent
this.endpointId = ""; this.endpointId = "";
this.inlineAlert.close(); this.inlineAlert.close();
} }
// Forcely refresh the view
forceRefreshView(duration: number): void {
// Reset timer
if (this.timerHandler) {
clearInterval(this.timerHandler);
}
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
setTimeout(() => {
if (this.timerHandler) {
clearInterval(this.timerHandler);
this.timerHandler = null;
}
}, duration);
}
openCreateEditTarget(editable: boolean, targetId?: number | string) { openCreateEditTarget(editable: boolean, targetId?: number | string) {
this.editable = editable; this.editable = editable;
// reset // reset
@ -238,7 +220,6 @@ export class CreateEditEndpointComponent
// Open the modal now // Open the modal now
this.open(); this.open();
this.editDisabled = true; this.editDisabled = true;
this.forceRefreshView(2000);
}, },
error => this.errorHandler.error(error) error => this.errorHandler.error(error)
); );
@ -308,12 +289,10 @@ export class CreateEditEndpointComponent
this.inlineAlert.showInlineSuccess({ this.inlineAlert.showInlineSuccess({
message: "DESTINATION.TEST_CONNECTION_SUCCESS" message: "DESTINATION.TEST_CONNECTION_SUCCESS"
}); });
this.forceRefreshView(2000);
this.testOngoing = false; this.testOngoing = false;
}, },
error => { error => {
this.inlineAlert.showInlineError("DESTINATION.TEST_CONNECTION_FAILURE"); this.inlineAlert.showInlineError("DESTINATION.TEST_CONNECTION_FAILURE");
this.forceRefreshView(2000);
this.testOngoing = false; this.testOngoing = false;
} }
); );
@ -340,12 +319,10 @@ export class CreateEditEndpointComponent
this.reload.emit(true); this.reload.emit(true);
this.onGoing = false; this.onGoing = false;
this.close(); this.close();
this.forceRefreshView(2000);
}, },
error => { error => {
this.onGoing = false; this.onGoing = false;
this.inlineAlert.showInlineError(error); this.inlineAlert.showInlineError(error);
this.forceRefreshView(2000);
} }
); );
} }
@ -383,12 +360,10 @@ export class CreateEditEndpointComponent
this.reload.emit(true); this.reload.emit(true);
this.close(); this.close();
this.onGoing = false; this.onGoing = false;
this.forceRefreshView(2000);
}, },
error => { error => {
this.inlineAlert.showInlineError(error); this.inlineAlert.showInlineError(error);
this.onGoing = false; this.onGoing = false;
this.forceRefreshView(2000);
} }
); );
} }

View File

@ -12,11 +12,11 @@
</div> </div>
</div> </div>
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<clr-datagrid [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow" (clrDgSelectedChange)="selectedChange()"> <clr-datagrid [clrDgLoading]="loading" [(clrDgSelected)]="selectedRow">
<clr-dg-action-bar> <clr-dg-action-bar>
<button type="button" class="btn btn-secondary" (click)="openModal()"><clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'DESTINATION.NEW_ENDPOINT' | translate}}</button> <button id="add" type="button" class="btn btn-secondary" (click)="openModal()"><clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'DESTINATION.NEW_ENDPOINT' | translate}}</button>
<button type="button" class="btn btn-secondary" [disabled]="!(selectedRow.length ===1)" (click)="editTargets(selectedRow)" ><clr-icon shape="pencil" size="16"></clr-icon>&nbsp;{{'DESTINATION.EDIT' | translate}}</button> <button id="edit" type="button" class="btn btn-secondary" [disabled]="!(selectedRow.length ===1)" (click)="editTargets(selectedRow)" ><clr-icon shape="pencil" size="16"></clr-icon>&nbsp;{{'DESTINATION.EDIT' | translate}}</button>
<button type="button" class="btn btn-secondary" [disabled]="!selectedRow.length" (click)="deleteTargets(selectedRow)"><clr-icon shape="times" size="16"></clr-icon>&nbsp;{{'DESTINATION.DELETE' | translate}}</button> <button id="delete" type="button" class="btn btn-secondary" [disabled]="!selectedRow.length" (click)="deleteTargets(selectedRow)"><clr-icon shape="times" size="16"></clr-icon>&nbsp;{{'DESTINATION.DELETE' | translate}}</button>
</clr-dg-action-bar> </clr-dg-action-bar>
<clr-dg-column [clrDgField]="'name'" class="flex-min-width">{{'DESTINATION.NAME' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'name'" class="flex-min-width">{{'DESTINATION.NAME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'status'" class="flex-min-width">{{'DESTINATION.STATUS' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'status'" class="flex-min-width">{{'DESTINATION.STATUS' | translate}}</clr-dg-column>

View File

@ -1,22 +1,17 @@
import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
import { By } from "@angular/platform-browser"; import { NO_ERRORS_SCHEMA } from "@angular/core";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
import { DebugElement } from "@angular/core";
import { EndpointComponent } from "./endpoint.component"; import { EndpointComponent } from "./endpoint.component";
import { FilterComponent } from "../../../shared/components/filter/filter.component";
import { ConfirmationDialogComponent } from "../../../shared/components/confirmation-dialog/confirmation-dialog.component";
import { CreateEditEndpointComponent } from "./create-edit-endpoint/create-edit-endpoint.component"; import { CreateEditEndpointComponent } from "./create-edit-endpoint/create-edit-endpoint.component";
import { InlineAlertComponent } from "../../../shared/components/inline-alert/inline-alert.component";
import { ErrorHandler } from "../../../shared/units/error-handler"; import { ErrorHandler } from "../../../shared/units/error-handler";
import { Endpoint } from "../../../shared/services"; import { Endpoint } from "../../../shared/services";
import { OperationService } from "../../../shared/components/operation/operation.service"; import { OperationService } from "../../../shared/components/operation/operation.service";
import { click } from "../../../shared/units/utils"; import { click } from "../../../shared/units/utils";
import { of } from "rxjs"; import { of } from "rxjs";
import { HttpClientTestingModule } from "@angular/common/http/testing";
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { AppConfigService } from '../../../services/app-config.service'; import { AppConfigService } from '../../../services/app-config.service';
import { SharedTestingModule } from "../../../shared/shared.module"; import { SharedTestingModule } from "../../../shared/shared.module";
import { EndpointDefaultService, EndpointService } from "../../../shared/services/endpoint.service"; import { ADAPTERS_MAP, EndpointService } from "../../../shared/services/endpoint.service";
import { delay } from "rxjs/operators";
describe("EndpointComponent (inline template)", () => { describe("EndpointComponent (inline template)", () => {
let adapterInfoMockData = { let adapterInfoMockData = {
@ -237,7 +232,7 @@ describe("EndpointComponent (inline template)", () => {
}; };
let fakedHttp = { let fakedHttp = {
get() { get() {
return of(adapterInfoMockData); return of(adapterInfoMockData).pipe(delay(0));
} }
}; };
let mockData: Endpoint[] = [ let mockData: Endpoint[] = [
@ -294,21 +289,6 @@ describe("EndpointComponent (inline template)", () => {
url: "https://4.4.4.4" url: "https://4.4.4.4"
} }
]; ];
let mockOne: Endpoint[] = [
{
id: 1,
credential: {
access_key: "admin",
access_secret: "",
type: "basic"
},
description: "test",
insecure: false,
name: "target_01",
type: "Harbor",
url: "https://10.117.4.151"
}
];
let mockAdapters = ['harbor', 'docker hub']; let mockAdapters = ['harbor', 'docker hub'];
let comp: EndpointComponent; let comp: EndpointComponent;
let fixture: ComponentFixture<EndpointComponent>; let fixture: ComponentFixture<EndpointComponent>;
@ -320,119 +300,109 @@ describe("EndpointComponent (inline template)", () => {
}; };
} }
}; };
const mockedEndpointService = {
let endpointService: EndpointService; getEndpoints(targetName: string) {
let spy: jasmine.Spy; if (targetName) {
let spyAdapter: jasmine.Spy; const endpoints: Endpoint[] = [];
let spyOnRules: jasmine.Spy; mockData.forEach( item => {
let spyOne: jasmine.Spy; if (item.name.indexOf(targetName) !== -1) {
endpoints.push(item);
}
});
return of(endpoints).pipe(delay(0));
}
return of(mockData).pipe(delay(0));
},
getAdapters() {
return of(mockAdapters).pipe(delay(0));
},
getEndpointWithReplicationRules() {
return of([]).pipe(delay(0));
},
getEndpoint(endPointId: number | string) {
if (endPointId) {
let endpoint: Endpoint;
mockData.forEach( item => {
if (item.id === endPointId) {
endpoint = item;
}
});
return of(endpoint).pipe(delay(0));
}
return of(mockData[0]).pipe(delay(0));
},
getAdapterText(adapter: string): string {
if (ADAPTERS_MAP && ADAPTERS_MAP[adapter]) {
return ADAPTERS_MAP[adapter];
}
return adapter;
}
};
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [SharedTestingModule, NoopAnimationsModule, HttpClientTestingModule], imports: [SharedTestingModule],
declarations: [ declarations: [
FilterComponent,
ConfirmationDialogComponent,
CreateEditEndpointComponent, CreateEditEndpointComponent,
InlineAlertComponent,
EndpointComponent EndpointComponent
], ],
providers: [ providers: [
ErrorHandler, ErrorHandler,
{ provide: EndpointService, useClass: EndpointDefaultService }, { provide: EndpointService, useValue: mockedEndpointService },
{ provide: OperationService }, { provide: OperationService },
{ provide: HttpClient, useValue: fakedHttp }, { provide: HttpClient, useValue: fakedHttp },
{ provide: AppConfigService, useValue: mockAppConfigService } { provide: AppConfigService, useValue: mockAppConfigService },
],
schemas: [
NO_ERRORS_SCHEMA
] ]
}); }).compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(EndpointComponent); fixture = TestBed.createComponent(EndpointComponent);
comp = fixture.componentInstance; comp = fixture.componentInstance;
endpointService = fixture.debugElement.injector.get(EndpointService); fixture.autoDetectChanges(true);
spy = spyOn(endpointService, "getEndpoints").and.returnValues(
of(mockData)
);
spyAdapter = spyOn(endpointService, "getAdapters").and.returnValue(
of(mockAdapters)
);
spyOnRules = spyOn(
endpointService,
"getEndpointWithReplicationRules"
).and.returnValue(of([]));
spyOne = spyOn(endpointService, "getEndpoint").and.returnValue(
of(mockOne[0])
);
fixture.detectChanges();
}); });
it("should retrieve endpoint data", () => { it("should retrieve endpoint data", async () => {
fixture.detectChanges(); await fixture.whenStable();
expect(spy.calls.any()).toBeTruthy(); const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');
expect(rows.length).toEqual(4);
}); });
it("should open create endpoint modal", waitForAsync(() => { it("should open edit endpoint modal", async () => {
fixture.detectChanges(); await fixture.whenStable();
fixture.whenStable().then(() => { const editButton: HTMLButtonElement = fixture.nativeElement.querySelector("#edit");
fixture.detectChanges(); comp.selectedRow = [mockData[0]] ;
comp.editTargets(mockOne); await fixture.whenStable();
fixture.detectChanges(); expect(editButton).toBeTruthy();
expect(comp.target.name).toEqual("target_01"); editButton.click();
editButton.dispatchEvent(new Event('click'));
await fixture.whenStable();
const nameInput: HTMLInputElement = fixture.nativeElement.querySelector("#destination_name");
expect(nameInput.value).toEqual('target_01');
}); });
}));
it("should filter endpoints by keyword", waitForAsync(() => { it("should filter endpoints by keyword", async () => {
fixture.detectChanges(); await fixture.whenStable();
fixture.whenStable().then(() => {
fixture.detectChanges();
comp.doSearchTargets("target_02"); comp.doSearchTargets("target_02");
fixture.detectChanges(); await fixture.whenStable();
const editButton: HTMLButtonElement = fixture.nativeElement.querySelector("#edit");
comp.selectedRow = [mockData[0]] ;
await fixture.whenStable();
editButton.click();
editButton.dispatchEvent(new Event('click'));
await fixture.whenStable();
expect(comp.targets.length).toEqual(1); expect(comp.targets.length).toEqual(1);
expect(comp.targets[0].name).toEqual('target_02');
}); });
})); it("should open creation endpoint", async () => {
await fixture.whenStable();
it("should render data", waitForAsync(() => { const addButton: HTMLButtonElement = fixture.nativeElement.querySelector("#add");
fixture.detectChanges(); expect(addButton).toBeTruthy();
fixture.whenStable().then(() => { addButton.click();
fixture.detectChanges(); addButton.dispatchEvent(new Event('click'));
let de: DebugElement = fixture.debugElement.query( await fixture.whenStable();
By.css("datagrid-cell") const nameInput: HTMLInputElement = fixture.nativeElement.querySelector("#destination_name");
); expect(nameInput).toBeTruthy();
expect(de).toBeTruthy();
let el: HTMLElement = de.nativeElement;
expect(el.textContent).toEqual("target_01");
}); });
}));
it("should open creation endpoint", waitForAsync(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
let de: DebugElement = fixture.debugElement.query(By.css("btn-link"));
expect(de).toBeTruthy();
fixture.detectChanges();
click(de);
fixture.detectChanges();
let deInput: DebugElement = fixture.debugElement.query(By.css("input"));
expect(deInput).toBeTruthy();
});
}));
it("should open to edit existing endpoint", waitForAsync(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
let de: DebugElement = fixture.debugElement.query(
del => del.classes["action-item"]
);
expect(de).toBeTruthy();
fixture.detectChanges();
click(de);
fixture.detectChanges();
let deInput: DebugElement = fixture.debugElement.query(By.css("input"));
expect(deInput).toBeTruthy();
let elInput: HTMLElement = deInput.nativeElement;
expect(elInput).toBeTruthy();
expect(elInput.textContent).toEqual("target_01");
});
}));
}); });

View File

@ -16,8 +16,6 @@ import {
OnInit, OnInit,
OnDestroy, OnDestroy,
ViewChild, ViewChild,
ChangeDetectionStrategy,
ChangeDetectorRef
} from "@angular/core"; } from "@angular/core";
import { Subscription, Observable, forkJoin, throwError as observableThrowError } from "rxjs"; import { Subscription, Observable, forkJoin, throwError as observableThrowError } from "rxjs";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
@ -45,7 +43,6 @@ import { EndpointService, HELM_HUB } from "../../../shared/services/endpoint.ser
selector: "hbr-endpoint", selector: "hbr-endpoint",
templateUrl: "./endpoint.component.html", templateUrl: "./endpoint.component.html",
styleUrls: ["./endpoint.component.scss"], styleUrls: ["./endpoint.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class EndpointComponent implements OnInit, OnDestroy { export class EndpointComponent implements OnInit, OnDestroy {
@ViewChild(CreateEditEndpointComponent) @ViewChild(CreateEditEndpointComponent)
@ -88,8 +85,7 @@ export class EndpointComponent implements OnInit, OnDestroy {
constructor(private endpointService: EndpointService, constructor(private endpointService: EndpointService,
private errorHandlerEntity: ErrorHandler, private errorHandlerEntity: ErrorHandler,
private translateService: TranslateService, private translateService: TranslateService,
private operationService: OperationService, private operationService: OperationService) {
private ref: ChangeDetectorRef) {
} }
ngOnInit(): void { ngOnInit(): void {
@ -102,17 +98,11 @@ export class EndpointComponent implements OnInit, OnDestroy {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
} }
selectedChange(): void {
this.forceRefreshView(5000);
}
retrieve(): void { retrieve(): void {
this.loading = true; this.loading = true;
this.selectedRow = []; this.selectedRow = [];
this.endpointService.getEndpoints(this.targetName).pipe(finalize(() => { this.endpointService.getEndpoints(this.targetName).pipe(finalize(() => {
this.loading = false; this.loading = false;
this.forceRefreshView(1000);
})) }))
.subscribe(targets => { .subscribe(targets => {
this.targets = targets || []; this.targets = targets || [];
@ -183,7 +173,6 @@ export class EndpointComponent implements OnInit, OnDestroy {
.pipe(finalize(() => { .pipe(finalize(() => {
this.selectedRow = []; this.selectedRow = [];
this.reload(true); this.reload(true);
this.forceRefreshView(2000);
})) }))
.subscribe((item) => { .subscribe((item) => {
}, error => { }, error => {
@ -219,22 +208,6 @@ export class EndpointComponent implements OnInit, OnDestroy {
} }
)); ));
} }
// Forcely refresh the view
forceRefreshView(duration: number): void {
// Reset timer
if (this.timerHandler) {
clearInterval(this.timerHandler);
}
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
setTimeout(() => {
if (this.timerHandler) {
clearInterval(this.timerHandler);
this.timerHandler = null;
}
}, duration);
}
getAdapterText(adapter: string): string { getAdapterText(adapter: string): string {
return this.endpointService.getAdapterText(adapter); return this.endpointService.getAdapterText(adapter);
} }

View File

@ -10,7 +10,7 @@ import { RequestQueryParams } from "./index";
import { Endpoint, ReplicationRule, PingEndpoint } from "./index"; import { Endpoint, ReplicationRule, PingEndpoint } from "./index";
import { catchError, map } from "rxjs/operators"; import { catchError, map } from "rxjs/operators";
const ADAPTERS_MAP = { export const ADAPTERS_MAP = {
"ali-acr": "Alibaba ACR", "ali-acr": "Alibaba ACR",
"aws-ecr": "Aws ECR", "aws-ecr": "Aws ECR",
"azure-acr": "Azure ACR", "azure-acr": "Azure ACR",