Repository: arenadata/adcm (language: python)

Issue:
ADCM-2929 skip pylint rule



Discussion / hints:

Fix patch:
diff --git a/web/src/app/shared/add-component/host.component.ts b/web/src/app/shared/add-component/host.component.ts
index 77946ceb91..063fd57ec6 100644
--- a/web/src/app/shared/add-component/host.component.ts
+++ b/web/src/app/shared/add-component/host.component.ts
@@ -134,7 +134,7 @@ export class HostComponent extends BaseFormDirective implements OnInit {
 
     if (provider && provider.actions) {
       const actions = provider.actions.filter((a) => a.button === ACTION_NAME);
-      if (actions && actions.length) {
+      if (actions && actions?.length) {
         this.action.inputData = { actions };
         this.onCancel();
         this.action.onClick();
@@ -163,7 +163,7 @@ export class HostComponent extends BaseFormDirective implements OnInit {
   }
 
   getNextPageClusters() {
-    const count = this.clusters$.getValue().length;
+    const count = this.clusters$.getValue()?.length;
     if (count === this.pageCluster * this.limit) {
       this.pageCluster++;
       this.getClusters();
@@ -171,7 +171,7 @@ export class HostComponent extends BaseFormDirective implements OnInit {
   }
 
   getNextPageProvider() {
-    const count = this.providers$.getValue().length;
+    const count = this.providers$.getValue()?.length;
     if (count === this.pageProvider * this.limit) {
       this.pageProvider++;
       this.getProviders();
diff --git a/web/src/app/shared/add-component/host2cluster.component.ts b/web/src/app/shared/add-component/host2cluster.component.ts
index 6797f2175c..c9d16642e2 100644
--- a/web/src/app/shared/add-component/host2cluster.component.ts
+++ b/web/src/app/shared/add-component/host2cluster.component.ts
@@ -78,7 +78,7 @@ export class Host2clusterComponent extends BaseFormDirective implements OnInit,
   selected: { [key: number]: boolean } = {};
 
   get disabled() {
-    return !Object.keys(this.selected).length;
+    return !Object.keys(this.selected)?.length;
   }
 
   ngOnInit() {
@@ -101,7 +101,7 @@ export class Host2clusterComponent extends BaseFormDirective implements OnInit,
         this.showForm = !r.count;
         this.displayMode = r.count > 0 ? 2 : 1;
         this.list = r.results;
-        if (this.listHosts?.options.length) this.allCbx.checked = false;
+        if (this.listHosts?.options?.length) this.allCbx.checked = false;
       });
   }
 
diff --git a/web/src/app/shared/components/actions/actions.component.spec.ts b/web/src/app/shared/components/actions/actions.component.spec.ts
index c070bba953..5570616a7e 100644
--- a/web/src/app/shared/components/actions/actions.component.spec.ts
+++ b/web/src/app/shared/components/actions/actions.component.spec.ts
@@ -39,7 +39,7 @@ describe('ActionsComponent', () => {
   it('Func `calcWidth` when the width of the window is greater than the buttons width sum', () => {
     const arr = [40, 30, 60];
     const result = component.calcWidth(1200, arr);
-    expect(result).toEqual([arr.length, 130, false]);
+    expect(result).toEqual([arr?.length, 130, false]);
   });
 
   it('Func `calcWidth` when the width of the window is less than the buttons width sum', () => {
diff --git a/web/src/app/shared/components/actions/actions.component.ts b/web/src/app/shared/components/actions/actions.component.ts
index 5c0eb312f3..64778ef990 100644
--- a/web/src/app/shared/components/actions/actions.component.ts
+++ b/web/src/app/shared/components/actions/actions.component.ts
@@ -61,7 +61,7 @@ export class ActionsComponent extends BaseDirective implements OnInit, AfterView
   @Input() cluster: { id: number; hostcomponent: string };
   @Input() set source(actions: IAction[]) {
     this.actions = actions;
-    if (!actions.length) this.render.setStyle(this.more.nativeElement, 'display', 'none');
+    if (!actions?.length) this.render.setStyle(this.more.nativeElement, 'display', 'none');
   }
 
   stateButtons = 0;
@@ -87,8 +87,8 @@ export class ActionsComponent extends BaseDirective implements OnInit, AfterView
   }
 
   ngAfterViewChecked(): void {
-    if (this.stateButtons !== this.buttons.length) {
-      this.stateButtons = this.buttons.length;
+    if (this.stateButtons !== this.buttons?.length) {
+      this.stateButtons = this.buttons?.length;
       setTimeout(() => this.onresize(), 0);
     }
   }
diff --git a/web/src/app/shared/components/actions/actions.directive.ts b/web/src/app/shared/components/actions/actions.directive.ts
index 38aec59433..eeaadbbe01 100644
--- a/web/src/app/shared/components/actions/actions.directive.ts
+++ b/web/src/app/shared/components/actions/actions.directive.ts
@@ -48,7 +48,7 @@ export class ActionsDirective {
     const act = model.actions[0];
     const isMulty = model.actions?.length > 1;
 
-    const width = isMulty || act.config?.config.length || act.hostcomponentmap?.length ? '90%' : '400px';
+    const width = isMulty || act.config?.config?.length || act.hostcomponentmap?.length ? '90%' : '400px';
     const title = act.ui_options?.disclaimer ? act.ui_options.disclaimer : isMulty ? 'Run actions?' : `Run an action [ ${act.display_name} ]?`;
 
     return {
diff --git a/web/src/app/shared/components/actions/master/master.component.spec.ts b/web/src/app/shared/components/actions/master/master.component.spec.ts
index 06d8335877..c416c08858 100644
--- a/web/src/app/shared/components/actions/master/master.component.spec.ts
+++ b/web/src/app/shared/components/actions/master/master.component.spec.ts
@@ -94,7 +94,7 @@ describe('MasterComponent', () => {
     fixture.detectChanges();
     const compHost: HTMLElement = fixture.debugElement.nativeElement;
     // expect(compHost.querySelector('i').textContent).toBe("Let's choose first");
-    const buttonsLength = compHost.querySelector('mat-action-list').getElementsByTagName('button').length;
+    const buttonsLength = compHost.querySelector('mat-action-list').getElementsByTagName('button')?.length;
     expect(buttonsLength).toBeGreaterThan(1);
   });
 
diff --git a/web/src/app/shared/components/actions/master/master.component.ts b/web/src/app/shared/components/actions/master/master.component.ts
index 05f75b7aa7..8b23e7f309 100644
--- a/web/src/app/shared/components/actions/master/master.component.ts
+++ b/web/src/app/shared/components/actions/master/master.component.ts
@@ -56,7 +56,7 @@ export class ActionMasterComponent extends BaseDirective implements DynamicCompo
   }
 
   ngOnInit(): void {
-    if (this.model.actions?.length === 1) this.choose(this.model.actions[0]);
+    if (this.model?.actions?.length === 1) this.choose(this.model.actions[0]);
   }
 
   choose(action: IAction) {
diff --git a/web/src/app/shared/components/upgrades/master/master.component.html b/web/src/app/shared/components/upgrades/master/master.component.html
index 2d46112fc6..d00cafdba6 100644
--- a/web/src/app/shared/components/upgrades/master/master.component.html
+++ b/web/src/app/shared/components/upgrades/master/master.component.html
@@ -31,9 +31,12 @@
     <mat-step [stepControl]="wcs.fields.form">
       <ng-template matStepLabel>Configuration</ng-template>
       <app-upgrade-master-config [upgrade]="upgrade" #wcs></app-upgrade-master-config>
-      <div class="controls">
-        <button mat-raised-button (click)="cancel()" color="primary">Cancel</button>
-        <button mat-raised-button matStepperNext color="primary">Next</button>
+      <div class="controls-container" [class.margin]="needMargin()">
+        <mat-checkbox [(ngModel)]="verbose">Verbose</mat-checkbox>
+        <div class="controls">
+          <button mat-raised-button (click)="cancel()" color="primary">Cancel</button>
+          <button mat-raised-button matStepperNext color="primary">Next</button>
+        </div>
       </div>
     </mat-step>
     <mat-step>
@@ -49,7 +52,7 @@
 </ng-template>
 
 <ng-template #controls let-value="value">
-  <div class="controls-container">
+  <div class="controls-container" [class.margin]="needMargin()">
 
     <mat-checkbox [(ngModel)]="verbose">Verbose</mat-checkbox>
 
@@ -60,9 +63,7 @@
       </ng-container>
 
       <button mat-raised-button (click)="cancel()" color="primary" tabindex="-1">Cancel</button>
-      <button mat-raised-button #runBtn (click)="run(value)" color="accent" [disabled]="isDisabled(value)" tabindex="1">
-        Run
-      </button>
+      <button mat-raised-button #runBtn (click)="run(value)" color="accent" [disabled]="isDisabled(value)" tabindex="1">Run</button>
     </div>
 
   </div>
diff --git a/web/src/app/shared/components/upgrades/master/master.component.scss b/web/src/app/shared/components/upgrades/master/master.component.scss
new file mode 100644
index 0000000000..96d0fd8a14
--- /dev/null
+++ b/web/src/app/shared/components/upgrades/master/master.component.scss
@@ -0,0 +1,18 @@
+.action-button {
+  background: none !important;
+  margin: 6px 0;
+
+  &:hover {
+    background: rgba(255, 255, 255, 0.04) !important;
+  }
+}
+
+.controls-container {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+
+  &.margin {
+    margin-top: 26px !important;
+  }
+}
diff --git a/web/src/app/shared/components/upgrades/master/master.component.spec.ts b/web/src/app/shared/components/upgrades/master/master.component.spec.ts
index ff72bdcdfc..7ebd618889 100644
--- a/web/src/app/shared/components/upgrades/master/master.component.spec.ts
+++ b/web/src/app/shared/components/upgrades/master/master.component.spec.ts
@@ -37,7 +37,10 @@ describe('MasterComponent', () => {
     TestBed.configureTestingModule({
       imports: [MatListModule],
       declarations: [MasterComponent, UpgradeMasterConfigComponent],
-      providers: [MasterService, { provide: ApiService, useValue: ApiServiceStub }, { provide: FieldService, useValue: FieldServiceStub }],
+      providers: [MasterService, {provide: ApiService, useValue: ApiServiceStub}, {
+        provide: FieldService,
+        useValue: FieldServiceStub
+      }],
       schemas: [NO_ERRORS_SCHEMA]
     }).compileComponents();
   });
@@ -52,20 +55,20 @@ describe('MasterComponent', () => {
   });
 
   it('input data for model property must be defined', () => {
-    component.model = { upgrades: [] };
+    component.model = {upgrades: []};
     fixture.detectChanges();
     expect(component.model).toBeDefined();
   });
 
   it('input data for model.actons property must be array', () => {
-    component.model = { upgrades: [] };
+    component.model = {upgrades: []};
     fixture.detectChanges();
     expect(component.model.upgrades).toBeDefined();
     expect(component.model.upgrades).toEqual(jasmine.any(Array));
   });
 
   it('should be show template if model.upgrades.length === 0', () => {
-    component.model = { upgrades: [] };
+    component.model = {upgrades: []};
     fixture.detectChanges();
     const compHost: HTMLElement = fixture.debugElement.nativeElement;
     expect(compHost.querySelector('p').textContent).toBe('No data for the upgrades!');
@@ -73,32 +76,83 @@ describe('MasterComponent', () => {
 
   it('should be show template for current upgrade if model.upgrades.length === 1 and config = null and host-map = null', () => {
     component.model = {
-      upgrades: [{ bundle_id: 1, config: {} as IConfig, description: '', do: 'url', from_edition: ['community'],
-        hostcomponentmap: [] as IActionParameter[], id: 1, license: '', license_url: '', max_strict: true, max_version: '',
-        min_strict: false, min_version: '', name: '', state_available: '', state_on_success: '', ui_options: {} as IUIOptions,
-        upgradable: true, url: '' }]
+      upgrades: [{
+        bundle_id: 1,
+        config: null,
+        description: '',
+        do: 'url',
+        from_edition: ['community'],
+        hostcomponentmap: null,
+        id: 1,
+        license: '',
+        license_url: '',
+        max_strict: true,
+        max_version: '',
+        min_strict: false,
+        min_version: '',
+        name: '',
+        state_available: '',
+        state_on_success: '',
+        ui_options: null,
+        upgradable: true,
+        url: ''
+      }]
     };
     fixture.detectChanges();
     const compHost: HTMLElement = fixture.debugElement.nativeElement;
     const cancelButton = compHost.querySelector('button[color="primary"]');
-    expect(cancelButton).toBeTruthy();
-    expect(cancelButton.textContent).toContain('Cancel');
     const runButton = compHost.querySelector('button[color="accent"]');
     expect(runButton).toBeTruthy();
+    expect(cancelButton).toBeTruthy();
+    expect(cancelButton.textContent).toContain('Cancel');
     expect(runButton.textContent).toContain('Run');
   });
 
   it('should be show upgrades list for choose current upgrade if model.upgrades.length > 1', () => {
     component.model = {
       upgrades: [
-        { bundle_id: 1, config: {} as IConfig, description: '', do: 'url', from_edition: ['community'],
-          hostcomponentmap: [] as IActionParameter[], id: 1, license: '', license_url: '', max_strict: true, max_version: '',
-          min_strict: false, min_version: '', name: '', state_available: '', state_on_success: '', ui_options: {} as IUIOptions,
-          upgradable: true, url: '' },
-        { bundle_id: 1, config: {} as IConfig, description: '', do: 'url', from_edition: ['community'],
-          hostcomponentmap: [] as IActionParameter[], id: 1, license: '', license_url: '', max_strict: true, max_version: '',
-          min_strict: false, min_version: '', name: '', state_available: '', state_on_success: '', ui_options: {} as IUIOptions,
-          upgradable: true, url: '' }
+        {
+          bundle_id: 1,
+          config: {} as IConfig,
+          description: '',
+          do: 'url',
+          from_edition: ['community'],
+          hostcomponentmap: [] as IActionParameter[],
+          id: 1,
+          license: '',
+          license_url: '',
+          max_strict: true,
+          max_version: '',
+          min_strict: false,
+          min_version: '',
+          name: '',
+          state_available: '',
+          state_on_success: '',
+          ui_options: {} as IUIOptions,
+          upgradable: true,
+          url: ''
+        },
+        {
+          bundle_id: 1,
+          config: {} as IConfig,
+          description: '',
+          do: 'url',
+          from_edition: ['community'],
+          hostcomponentmap: [] as IActionParameter[],
+          id: 1,
+          license: '',
+          license_url: '',
+          max_strict: true,
+          max_version: '',
+          min_strict: false,
+          min_version: '',
+          name: '',
+          state_available: '',
+          state_on_success: '',
+          ui_options: {} as IUIOptions,
+          upgradable: true,
+          url: ''
+        }
       ]
     };
     fixture.detectChanges();
@@ -111,7 +165,10 @@ describe('MasterComponent', () => {
   it('should be show template for current upgrade if config exist only', () => {
     component.model = {
       upgrades: [
-        { bundle_id: 1, config: {config: [
+        {
+          bundle_id: 1,
+          config: {
+            config: [
               {
                 type: 'string',
                 name: 'test',
@@ -125,10 +182,26 @@ describe('MasterComponent', () => {
                 activatable: false,
                 group_config: null,
               }
-            ]} as IConfig, description: '', do: 'url', from_edition: ['community'],
-          hostcomponentmap: [] as IActionParameter[], id: 1, license: '', license_url: '', max_strict: true, max_version: '',
-          min_strict: false, min_version: '', name: '', state_available: '', state_on_success: '', ui_options: {} as IUIOptions,
-          upgradable: true, url: '' }
+            ]
+          } as IConfig,
+          description: '',
+          do: 'url',
+          from_edition: ['community'],
+          hostcomponentmap: null,
+          id: 1,
+          license: '',
+          license_url: '',
+          max_strict: true,
+          max_version: '',
+          min_strict: false,
+          min_version: '',
+          name: '',
+          state_available: '',
+          state_on_success: '',
+          ui_options: {} as IUIOptions,
+          upgradable: true,
+          url: ''
+        }
       ]
     };
     fixture.detectChanges();
@@ -141,10 +214,12 @@ describe('MasterComponent', () => {
 
   it('should be show template for current upgrade if host-map exist only', () => {
     component.model = {
-      upgrades: [{ bundle_id: 1, config: {} as IConfig, description: '', do: 'url', from_edition: ['community'],
-        hostcomponentmap:  [{ component: '', action: 'add', service: '' }] as IActionParameter[], id: 1, license: '',
+      upgrades: [{
+        bundle_id: 1, config: null, description: '', do: 'url', from_edition: ['community'],
+        hostcomponentmap: [{component: '', action: 'add', service: ''}] as IActionParameter[], id: 1, license: '',
         license_url: '', max_strict: true, max_version: '', min_strict: false, min_version: '', name: '',
-        state_available: '', state_on_success: '', ui_options: {} as IUIOptions, upgradable: true, url: '' }]
+        state_available: '', state_on_success: '', ui_options: {} as IUIOptions, upgradable: true, url: ''
+      }]
     };
     fixture.detectChanges();
     const de = fixture.debugElement.nativeElement;
@@ -156,7 +231,9 @@ describe('MasterComponent', () => {
 
   it('should be show template for current upgrade with config and host-map', () => {
     component.model = {
-      upgrades: [{ bundle_id: 1, config: {config: [
+      upgrades: [{
+        bundle_id: 1, config: {
+          config: [
             {
               type: 'string',
               name: 'test',
@@ -170,10 +247,12 @@ describe('MasterComponent', () => {
               activatable: false,
               group_config: null,
             }
-          ]} as IConfig, description: '', do: 'url', from_edition: ['community'],
-        hostcomponentmap:  [{ component: '', action: 'add', service: '' }] as IActionParameter[], id: 1, license: '',
+          ]
+        } as IConfig, description: '', do: 'url', from_edition: ['community'],
+        hostcomponentmap: [{component: '', action: 'add', service: ''}] as IActionParameter[], id: 1, license: '',
         license_url: '', max_strict: true, max_version: '', min_strict: false, min_version: '', name: '',
-        state_available: '', state_on_success: '', ui_options: {} as IUIOptions, upgradable: true, url: '' }]
+        state_available: '', state_on_success: '', ui_options: {} as IUIOptions, upgradable: true, url: ''
+      }]
     };
     fixture.detectChanges();
     const de = fixture.debugElement.nativeElement;
@@ -193,24 +272,24 @@ describe('MasterComponent', () => {
     const service = fixture.debugElement.injector.get(MasterService);
 
     const config = {
-      form: new FormGroup({ string_ctr: new FormControl('string_test'), bool_ctr: new FormControl(true) }),
+      form: new FormGroup({string_ctr: new FormControl('string_test'), bool_ctr: new FormControl(true)}),
       rawConfig: {
         config: [
-          { name: 'string_ctr', type: 'string', value: 'string_test' },
-          { name: 'bool_ctr', type: 'boolean', value: true }
+          {name: 'string_ctr', type: 'string', value: 'string_test'},
+          {name: 'bool_ctr', type: 'boolean', value: true}
         ]
       }
     } as ConfigFieldsComponent;
 
-    const result = service.parseData({ config });
-    expect(result).toEqual({ config: { string_ctr: 'string_test', bool_ctr: true }, hc: undefined, attr: undefined });
+    const result = service.parseData({config});
+    expect(result).toEqual({config: {string_ctr: 'string_test', bool_ctr: true}, hc: undefined, attr: undefined});
   });
 
   it('check value when ServiceHostComponent exist', () => {
     const service = fixture.debugElement.injector.get(MasterService);
-    const hc = [{ host_id: 1, service_id: 4, component_id: 1, id: 9 }];
-    const hostmap = { statePost: { data: hc } } as ServiceHostComponent;
-    const result = service.parseData({ hostmap });
-    expect(result).toEqual({ config: undefined, hc, attr: undefined });
+    const hc = [{host_id: 1, service_id: 4, component_id: 1, id: 9}];
+    const hostmap = {statePost: {data: hc}} as ServiceHostComponent;
+    const result = service.parseData({hostmap});
+    expect(result).toEqual({config: undefined, hc, attr: undefined});
   });
 });
diff --git a/web/src/app/shared/components/upgrades/master/master.component.ts b/web/src/app/shared/components/upgrades/master/master.component.ts
index 626d06fd39..3f56dabc38 100644
--- a/web/src/app/shared/components/upgrades/master/master.component.ts
+++ b/web/src/app/shared/components/upgrades/master/master.component.ts
@@ -19,24 +19,7 @@ import { UpgradeParameters } from "@app/shared/components/upgrades/upgrade.direc
 @Component({
   selector: 'app-master',
   templateUrl: './master.component.html',
-  styles: [
-    `
-      .action-button {
-        background: none !important;
-        margin: 6px 0;
-
-        &:hover {
-          background: rgba(255, 255, 255, 0.04) !important;
-        }
-      }
-
-      .controls-container {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-      }
-    `,
-  ],
+  styleUrls: ['./master.component.scss'],
   providers: [MasterService],
 })
 export class UpgradeMasterComponent extends BaseDirective implements DynamicComponent, OnInit {
@@ -86,4 +69,8 @@ export class UpgradeMasterComponent extends BaseDirective implements DynamicComp
   cancel() {
     this.event.emit({ name: 'cancel' });
   }
+
+  needMargin() {
+    return !(this?.model?.upgrades[0]?.config?.config?.length > 0 && this?.model?.upgrades[0]?.hostcomponentmap?.length > 0);
+  }
 }
diff --git a/web/src/app/shared/components/upgrades/upgrade.directive.ts b/web/src/app/shared/components/upgrades/upgrade.directive.ts
index 1734b36e12..8deee5432c 100644
--- a/web/src/app/shared/components/upgrades/upgrade.directive.ts
+++ b/web/src/app/shared/components/upgrades/upgrade.directive.ts
@@ -55,7 +55,7 @@ export class UpgradesDirective extends BaseDirective {
   }
 
   get hasConfig(): boolean {
-    return this?.inputData?.config.config?.length > 0
+    return this?.inputData?.config?.config?.length > 0
   }
 
   get hasHostComponent(): boolean {
@@ -78,7 +78,7 @@ export class UpgradesDirective extends BaseDirective {
         id: this.clusterId,
         hostcomponent: this.hc,
       },
-      upgrades: [this.inputData]
+      upgrades: [this.inputData],
     }
 
     dialogModel =  {
@@ -98,6 +98,14 @@ export class UpgradesDirective extends BaseDirective {
         this.runOldUpgrade(data);
       }
     } else {
+      if (!this.hasConfig && !this.hasHostComponent) {
+        dialogModel.maxWidth = '1400px';
+        dialogModel.width = '400px';
+        dialogModel.height = '180px';
+        dialogModel.data.title = 'Are you sure you want to upgrade?'
+        dialogModel.data.text = 'The cluster will be prepared for upgrade';
+      }
+
       this.dialog.open(DialogComponent, dialogModel);
     }
   }
@@ -153,7 +161,7 @@ export class UpgradesDirective extends BaseDirective {
             )
         )
       )
-      .subscribe(row => this.refresh.emit({ cmd: 'refresh', row }));
+      .subscribe(row => this.refresh.emit({cmd: 'refresh', row}));
   }
 
   fork(item: IUpgrade) {
@@ -175,17 +183,17 @@ export class UpgradesDirective extends BaseDirective {
                 name: hc.component,
                 parent_name: hc.service,
                 limit: 50,
-                offset:0
+                offset: 0
               };
 
-            // fix later
-            this.service.getPrototype('prototype', params).subscribe((prototype): any => {
+              // fix later
+              this.service.getPrototype('prototype', params).subscribe((prototype): any => {
                 if (prototype[0]) {
                   cluster.component.push(prototype[0]);
                   this.hc = cluster;
                 }
 
-                if (hostComponentMap.length === index+1) {
+                if (hostComponentMap.length === index + 1) {
                   if (!this.hc) this.hc = cluster;
                   this.prepare();
                 }
diff --git a/web/src/app/shared/configuration/field/field.component.html b/web/src/app/shared/configuration/field/field.component.html
index 0fede65b88..c381e6e613 100644
--- a/web/src/app/shared/configuration/field/field.component.html
+++ b/web/src/app/shared/configuration/field/field.component.html
@@ -48,7 +48,7 @@
     </span>
   </ng-container>
 </div>
-<mat-list class="compare-list-container" dense *ngIf="!options.hidden && options.compare.length">
+<mat-list class="compare-list-container" dense *ngIf="!options.hidden && options.compare?.length">
   <mat-list-item *ngFor="let item of options.compare" class="compare-list">
     <mat-icon matListIcon [style.backgroundColor]="item.color">more_vert</mat-icon>
     <span class="label">
diff --git a/web/src/app/shared/host-components-map/much-2-many/much-2-many.component.html b/web/src/app/shared/host-components-map/much-2-many/much-2-many.component.html
index 7b8f77d3f1..af63450331 100644
--- a/web/src/app/shared/host-components-map/much-2-many/much-2-many.component.html
+++ b/web/src/app/shared/host-components-map/much-2-many/much-2-many.component.html
@@ -6,7 +6,7 @@
         [style.color]="model.color">{{ model.name }}</span>
     </button>
     <button mat-raised-button color="primary"
-      (click)="toggleRelations()">{{ model.limit?.length ? setNotify() : model.relations.length }}</button>
+      (click)="toggleRelations()">{{ model.limit?.length ? setNotify() : model.relations?.length }}</button>
   </div>
   <div [ngClass]="{'relations-list':true, 'show': model.relations.length && isShow, 'disabled': isHostDisabled()}">
     <div *ngFor="let rel of model.relations">
diff --git a/web/src/app/shared/host-components-map/services2hosts/service-host.component.spec.ts b/web/src/app/shared/host-components-map/services2hosts/service-host.component.spec.ts
index 5b26c73042..2d84023761 100644
--- a/web/src/app/shared/host-components-map/services2hosts/service-host.component.spec.ts
+++ b/web/src/app/shared/host-components-map/services2hosts/service-host.component.spec.ts
@@ -93,7 +93,7 @@ describe('Service Host Map Component', () => {
     expect(component).toBeDefined();
   });
 
-  it('should display compnents and hosts as button', () => {
+  it('should display components and hosts as button', () => {
     initDefault(genData());
     const dom = domFirstElems(fixture.nativeElement);
     const components = dom.components;
@@ -133,7 +133,7 @@ describe('Service Host Map Component', () => {
     const host_isLinked = () => host.querySelector('.m2m').classList.contains('linked');
 
     // start: host - select
-    host_btn.click();
+    host_btn.click(); // added class .selected
     fixture.detectChanges();
     expect(host_isSelect()).toBeTrue();
 


Test patch:
diff --git a/tests/ui_tests/test_cluster_list_page.py b/tests/ui_tests/test_cluster_list_page.py
index 6fb0fd97eb..6d64bba787 100644
--- a/tests/ui_tests/test_cluster_list_page.py
+++ b/tests/ui_tests/test_cluster_list_page.py
@@ -109,7 +109,7 @@
 
 
 # pylint: disable=redefined-outer-name,unused-argument,too-many-lines,too-many-public-methods
-# pylint: disable=too-many-arguments,too-many-boolean-expressions,too-many-branches,too-many-nested-blocks
+# pylint: disable=too-many-arguments,too-many-boolean-expressions,too-many-branches,too-many-nested-blocks,too-many-locals
 
 pytestmark = pytest.mark.usefixtures("login_to_adcm_over_api")