/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * 'License'); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { AlertsListComponent } from './alerts-list.component';
import { ComponentFixture, async, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { Component, Input, Directive } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
import { SearchService } from 'app/service/search.service';
import { UpdateService } from 'app/service/update.service';
import { ConfigureTableService } from 'app/service/configure-table.service';
import { AlertsService } from 'app/service/alerts.service';
import { ClusterMetaDataService } from 'app/service/cluster-metadata.service';
import { SaveSearchService } from 'app/service/save-search.service';
import { MetaAlertService } from 'app/service/meta-alert.service';
import { GlobalConfigService } from 'app/service/global-config.service';
import { DialogService } from 'app/service/dialog.service';
import { TIMESTAMP_FIELD_NAME } from 'app/utils/constants';
import { By } from '@angular/platform-browser';
import { Observable, Subject, of, throwError } from 'rxjs';
import { Filter } from 'app/model/filter';
import { QueryBuilder, FilteringMode } from './query-builder';
import { SearchResponse } from 'app/model/search-response';
import { AutoPollingService } from './auto-polling/auto-polling.service';
import { Router, NavigationStart } from '@angular/router';
import { Alert } from 'app/model/alert';
import { AlertSource } from 'app/model/alert-source';
import { SearchRequest } from 'app/model/search-request';
import { query } from '@angular/core/src/render3';
import { RestError } from 'app/model/rest-error';
import { DialogType } from 'app/shared/metron-dialog/metron-dialog.component';
import { SaveSearch } from 'app/model/save-search';

@Component({
  selector: 'app-auto-polling',
  template: '<div></div>',
})
class MockAutoPollingComponent {}

@Component({
  selector: 'app-configure-rows',
  template: '<div></div>',
})
class MockConfigureRowsComponent {
  @Input() refreshInterval = 0;
  @Input() srcElement = {};
  @Input() pageSize = 0;
}

@Component({
  selector: 'app-modal-loading-indicator',
  template: '<div></div>',
})
class MockModalLoadingIndicatorComponent {
  @Input() show = false;
}

@Component({
  selector: 'app-time-range',
  template: '<div></div>',
})
class MockTimeRangeComponent {
  @Input() disabled = false;
  @Input() selectedTimeRange = {};
}

@Directive({
  selector: '[appAceEditor]',
})
class MockAceEditorDirective {
  @Input() text = '';
}

@Component({
  selector: 'app-alert-filters',
  template: '<div></div>',
})
class MockAlertFilterComponent {
  @Input() facets = [];
}

@Component({
  selector: 'app-group-by',
  template: '<div></div>',
})
class MockGroupByComponent {
  @Input() facets = [];
}

@Component({
  selector: 'app-table-view',
  template: '<div></div>',
})
class MockTableViewComponent {
  @Input() alerts = [];
  @Input() pagination = {};
  @Input() alertsColumnsToDisplay = [];
  @Input() selectedAlerts = [];
}

@Component({
  selector: 'app-tree-view',
  template: '<div></div>',
})
class MockTreeViewComponent {
  @Input() alerts = [];
  @Input() pagination = {};
  @Input() alertsColumnsToDisplay = [];
  @Input() selectedAlerts = [];
  @Input() globalConfig = {};
  @Input() query = '';
  @Input() groups = [];
}


describe('AlertsListComponent', () => {

  let component: AlertsListComponent;
  let fixture: ComponentFixture<AlertsListComponent>;

  let queryBuilder: QueryBuilder;
  let searchService: SearchService;

  beforeEach(async(() => {

    const searchResponseFake = new SearchResponse();
    searchResponseFake.facetCounts = {};

    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes([{path: 'alerts-list', component: AlertsListComponent}]),
      ],
      declarations: [
        AlertsListComponent,
        MockAutoPollingComponent,
        MockModalLoadingIndicatorComponent,
        MockTimeRangeComponent,
        MockAceEditorDirective,
        MockConfigureRowsComponent,
        MockAlertFilterComponent,
        MockGroupByComponent,
        MockTableViewComponent,
        MockTreeViewComponent,
      ],
      providers: [
        { provide: SearchService, useClass: () => { return {
          search: () => of(searchResponseFake),
        } } },
        { provide: UpdateService, useClass: () => { return {
          alertChanged$: new Observable(),
          updateAlertState: () => of(),
        } } },
        { provide: ConfigureTableService, useClass: () => { return {
          getTableMetadata: () => new Observable(),
          tableChanged$: new Observable(),
          saveTableMetaData: () => of(),
        } } },
        { provide: AlertsService, useClass: () => { return {} } },
        { provide: ClusterMetaDataService, useClass: () => { return {
          getDefaultColumns: () => new Observable(),
        } } },
        { provide: SaveSearchService, useClass: () => { return {
          loadSavedSearch$: new Observable(),
          setCurrentQueryBuilderAndTableColumns: () => {},
          fireLoadSavedSearch: (savedSearch: any) => {
            this.loadSavedSearch$.next(savedSearch);
          },
          saveAsRecentSearches: () => of(null),
        } } },
        { provide: MetaAlertService, useClass: () => { return {
          alertChanged$: new Observable(),
          selectedAlerts: []
        } } },
        { provide: GlobalConfigService, useClass: () => { return {
          get: () => new Observable(),
        } } },
        { provide: DialogService, useClass: () => { return {} } },
        { provide: QueryBuilder, useClass: () => { return {
          filters: [],
          query: '*',
          searchRequest: () => {
            return new SearchResponse();
          },
          addOrUpdateFilter: () => {},
          clearSearch: () => {},
          isTimeStampFieldPresent: () => {},
          getManualQuery: () => {},
          setManualQuery: () => {},
          getFilteringMode: () => {},
          setFilteringMode: () => {},
          generateNameForSearchRequest: () => '',
          setSearch: () => {},
        } } },
        { provide: AutoPollingService, useClass: () => { return {
          data: new Subject<SearchResponse>(),
          getIsCongestion: () => {},
          getInterval: () => {},
          getIsPollingActive: () => {},
          dropNextAndContinue: () => {},
          onDestroy: () => {},
          setSuppression: () => {},
          setInterval: () => {},
        } } },
      ]
    })
    .compileComponents();

    queryBuilder = TestBed.get(QueryBuilder);
    searchService = TestBed.get(SearchService);
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(AlertsListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should exist', () => {
    expect(component).toBeTruthy();
  });

  it('should get global config on init', () => {
    const globalConfig = { 'source.type.field': 'test'}
    const globalConfigService = TestBed.get(GlobalConfigService);
    spyOn(globalConfigService, 'get').and.returnValue(of(globalConfig));
    component.ngOnInit();
    component.alertsColumns = [
      { name: 'ip:source', type: 'testType' },
      { name: 'source:type', type: 'testType' }
    ];
    fixture.detectChanges();
    expect(component.globalConfig).toEqual(globalConfig);
    expect(component.alertsColumns.length).toEqual(2);
  });

  it('should clear selected alerts if NavigationStart', () => {
    const router = TestBed.get(Router);
    spyOn(router, 'events').and.returnValue({url: '/alerts-list'} instanceof NavigationStart);
    component.ngOnInit();
    fixture.detectChanges();
    expect(component.selectedAlerts).toEqual([]);
  });

  it('should set default query time range', () => {
    expect(component.selectedTimeRange instanceof Filter).toBeTruthy();
    expect(component.selectedTimeRange.value).toBe('last-15-minutes');
  });

  it('default query time range from date should be set', () => {
    expect(component.selectedTimeRange.dateFilterValue.fromDate).toBeTruthy();
  });

  it('default query time range to date should be set', () => {
    expect(component.selectedTimeRange.dateFilterValue.toDate).toBeTruthy();
  });

  it('shows subtotals in view when onTreeViewChange is truthy', () => {
    component.onTreeViewChange(4);
    fixture.detectChanges();
    let subtotal = fixture.nativeElement.querySelector('[data-qe-id="alert-subgroup-total"]');
    expect(subtotal.textContent).toEqual('Alerts in Groups (4)');

    component.onTreeViewChange(0);
    fixture.detectChanges();
    expect(fixture.nativeElement.querySelector('[data-qe-id="alert-subgroup-total"]')).toBeNull();
  });

  describe('filtering by query builder or manual query', () => {
    it('should be able to toggle the query builder mode', () => {
      spyOn(component, 'setSearchRequestSize');
      spyOn(queryBuilder, 'setFilteringMode');

      queryBuilder.getFilteringMode = () => FilteringMode.BUILDER;

      component.toggleQueryBuilderMode();
      expect(queryBuilder.setFilteringMode).toHaveBeenCalledWith(FilteringMode.MANUAL);

      queryBuilder.getFilteringMode = () => FilteringMode.MANUAL;

      component.toggleQueryBuilderMode();
      expect(queryBuilder.setFilteringMode).toHaveBeenCalledWith(FilteringMode.BUILDER);
    });

    it('isQueryBuilderModeManual should return true if queryBuilder is in manual mode', () => {
      queryBuilder.getFilteringMode = () => FilteringMode.MANUAL;
      expect(component.isQueryBuilderModeManual()).toBe(true);

      queryBuilder.getFilteringMode = () => FilteringMode.BUILDER;
      expect(component.isQueryBuilderModeManual()).toBe(false);
    });

    it('should show manual input dom element depending on mode', () => {
      let input = fixture.debugElement.query(By.css('[data-qe-id="manual-query-input"]'));

      expect(input).toBeFalsy();

      queryBuilder.getFilteringMode = () => FilteringMode.MANUAL;
      fixture.detectChanges();
      input = fixture.debugElement.query(By.css('[data-qe-id="manual-query-input"]'));

      expect(input).toBeTruthy();

      queryBuilder.getFilteringMode = () => FilteringMode.BUILDER;
      fixture.detectChanges();
      input = fixture.debugElement.query(By.css('[data-qe-id="manual-query-input"]'));

      expect(input).toBeFalsy();
    });

    it('should bind default manual query from query builder', () => {
      spyOn(queryBuilder, 'getManualQuery').and.returnValue('test manual query string')

      queryBuilder.getFilteringMode = () => FilteringMode.MANUAL;
      fixture.detectChanges();
      let input: HTMLInputElement = fixture.debugElement.query(By.css('[data-qe-id="manual-query-input"]')).nativeElement;

      expect(input.value).toBe('test manual query string');
    });

    it('should pass the manual query value to the query builder when editing mode is manual', fakeAsync(() => {
      spyOn(queryBuilder, 'setManualQuery');

      queryBuilder.getFilteringMode = () => FilteringMode.MANUAL;
      fixture.detectChanges();

      const input = fixture.debugElement.query(By.css('[data-qe-id="manual-query-input"]'));
      const el = input.nativeElement;

      el.value = 'test';
      (el as HTMLElement).dispatchEvent(new Event('keyup'));
      fixture.detectChanges();
      tick(300);

      expect(queryBuilder.setManualQuery).toHaveBeenCalledWith('test');
    }));
  });

  describe('handling pending search requests', () => {
    it('should set pendingSearch on search', () => {
      spyOn(searchService, 'search').and.returnValue(of(new SearchResponse()));
      spyOn(component, 'saveCurrentSearch');
      spyOn(component, 'setSearchRequestSize');
      spyOn(component, 'setSelectedTimeRange');
      spyOn(component, 'createGroupFacets');

      component.search();
      expect(component.pendingSearch).toBeTruthy();
    });

    it('should clear pendingSearch on search success', (done) => {
      const fakeObservable = new Subject();
      spyOn(searchService, 'search').and.returnValue(fakeObservable);
      spyOn(component, 'saveCurrentSearch');
      spyOn(component, 'setSearchRequestSize');
      spyOn(component, 'setSelectedTimeRange');
      spyOn(component, 'createGroupFacets');

      component.search();

      setTimeout(() => {
        fakeObservable.next(new SearchResponse());
      }, 0);

      fakeObservable.subscribe(() => {
        expect(component.pendingSearch).toBe(null);
        done();
      })
    });
  });

  describe('stale data state', () => {
    it('should set staleDataState flag to true on filter change', () => {
      expect(component.staleDataState).toBe(false);
      component.onAddFilter(new Filter('ip_src_addr', '0.0.0.0'));
      expect(component.staleDataState).toBe(true);
    });

    it('should set staleDataState flag to true on filter clearing', () => {
      spyOn(component, 'setSearchRequestSize');

      expect(component.staleDataState).toBe(false);
      component.onClear();
      expect(component.staleDataState).toBe(true);
    });

    it('should set staleDataState flag to true on timerange change', () => {
      expect(component.staleDataState).toBe(false);
      component.onTimeRangeChange(new Filter(TIMESTAMP_FIELD_NAME, 'this-year'));
      expect(component.staleDataState).toBe(true);
    });

    it('should set staleDataState flag to false when the query resolves', () => {
      spyOn(searchService, 'search').and.returnValue(of(new SearchResponse()));
      spyOn(component, 'saveCurrentSearch');
      spyOn(component, 'setSearchRequestSize');
      spyOn(component, 'setSelectedTimeRange');
      spyOn(component, 'createGroupFacets');

      component.staleDataState = true;
      component.search();
      expect(component.staleDataState).toBe(false);
    });

    it('should set stale date true when query changes in manual mode', fakeAsync(() => {
      queryBuilder.getFilteringMode = () => FilteringMode.MANUAL;

      fixture.detectChanges();
      const input = fixture.debugElement.query(By.css('[data-qe-id="manual-query-input"]'));
      const el = input.nativeElement;

      el.value = 'test';
      (el as HTMLElement).dispatchEvent(new Event('keyup'));
      fixture.detectChanges();
      tick(300);

      expect(component.staleDataState).toBe(true);
    }));

    it('should show warning if data is in a stale state', () => {
      expect(fixture.debugElement.query(By.css('[data-qe-id="staleDataWarning"]'))).toBe(null);

      component.staleDataState = true;
      fixture.detectChanges();

      expect(fixture.debugElement.query(By.css('[data-qe-id="staleDataWarning"]'))).toBeTruthy();
    });

  });

  describe('auto polling', () => {
    it('should refresh view on data emit', () => {
      const fakeResponse = new SearchResponse();
      spyOn(component, 'setData');

      TestBed.get(AutoPollingService).data.next(fakeResponse);

      expect(component.setData).toHaveBeenCalledWith(fakeResponse);
    });

    it('should set staleDataState false on auto polling refresh', () => {
      spyOn(component, 'setData');
      component.staleDataState = true;

      TestBed.get(AutoPollingService).data.next(new SearchResponse());

      expect(component.staleDataState).toBe(false);
    });

    it('should show warning on auto polling congestion', () => {
      expect(fixture.debugElement.query(By.css('[data-qe-id="pollingCongestionWarning"]'))).toBeFalsy();

      TestBed.get(AutoPollingService).getIsCongestion = () => true;
      fixture.detectChanges();

      expect(fixture.debugElement.query(By.css('[data-qe-id="pollingCongestionWarning"]'))).toBeTruthy();

      TestBed.get(AutoPollingService).getIsCongestion = () => false;
      fixture.detectChanges();

      expect(fixture.debugElement.query(By.css('[data-qe-id="pollingCongestionWarning"]'))).toBeFalsy();
    });

    it('should pass refresh interval to row config component', () => {
      TestBed.get(AutoPollingService).getInterval = () => 44;
      fixture.detectChanges();

      expect(fixture.debugElement.query(By.directive(MockConfigureRowsComponent)).componentInstance.refreshInterval).toBe(44);
    });

    it('should drop pending auto polling result if user trigger search request manually', () => {
      const autoPollingSvc = TestBed.get(AutoPollingService);
      spyOn(autoPollingSvc, 'dropNextAndContinue');
      spyOn(component, 'setSearchRequestSize');

      autoPollingSvc.getIsPollingActive = () => false;
      component.search()

      expect(autoPollingSvc.dropNextAndContinue).not.toHaveBeenCalled();

      autoPollingSvc.getIsPollingActive = () => true;
      component.search()

      expect(autoPollingSvc.dropNextAndContinue).toHaveBeenCalled();
    });

    it('should show different stale data warning when polling is active', () => {
      const autoPollingSvc = TestBed.get(AutoPollingService);

      autoPollingSvc.getIsPollingActive = () => false;
      const warning = component.getStaleDataWarning();

      autoPollingSvc.getIsPollingActive = () => true;
      const warningWhenPolling = component.getStaleDataWarning();

      expect(warning).not.toEqual(warningWhenPolling);
    });

    it('should show getIsCongestion scennarios', () => {
      const autoPollingSvc = TestBed.get(AutoPollingService);

      autoPollingSvc.getIsCongestion = () => false;
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('[data-qe-id="pollingCongestionWarning"]'))).toBeFalsy();

      autoPollingSvc.getIsCongestion = () => true;
      fixture.detectChanges();
      expect(fixture.debugElement.query(By.css('[data-qe-id="pollingCongestionWarning"]'))).toBeTruthy();

    });

    it('should suppress polling when user select alerts', () => {
      const autoPollingSvc = TestBed.get(AutoPollingService);
      spyOn(autoPollingSvc, 'setSuppression');

      component.onSelectedAlertsChange([{ source: { metron_alert: [] } }]);

      expect(autoPollingSvc.setSuppression).toHaveBeenCalledWith(true);
    });

    it('should restore polling from suppression when user deselect alerts', () => {
      const autoPollingSvc = TestBed.get(AutoPollingService);
      spyOn(autoPollingSvc, 'setSuppression');

      component.onSelectedAlertsChange([]);

      expect(autoPollingSvc.setSuppression).toHaveBeenCalledWith(false);
    });

    it('should suppress polling when open details pane', () => {
      const autoPollingSvc = TestBed.get(AutoPollingService);
      const router = TestBed.get(Router);
      spyOn(router, 'navigate').and.returnValue(true);
      spyOn(router, 'navigateByUrl').and.returnValue(true);
      spyOn(autoPollingSvc, 'setSuppression');

      component.showConfigureTable();

      expect(autoPollingSvc.setSuppression).toHaveBeenCalledWith(true);
    });

    it('should suppress polling when open column config pane', () => {
      const router = TestBed.get(Router);
      const autoPollingSvc = TestBed.get(AutoPollingService);
      spyOn(router, 'navigate');
      spyOn(router, 'navigateByUrl');
      spyOn(autoPollingSvc, 'setSuppression');

      const fakeAlert = new Alert();
      fakeAlert.source = new AlertSource();

      component.showDetails(fakeAlert);

      expect(autoPollingSvc.setSuppression).toHaveBeenCalledWith(true);
    });

    it('should suppress polling when open Saved Searches pane', () => {
      const router = TestBed.get(Router);
      const autoPollingSvc = TestBed.get(AutoPollingService);
      spyOn(router, 'navigate');
      spyOn(router, 'navigateByUrl');
      spyOn(autoPollingSvc, 'setSuppression');

      component.showSavedSearches();

      expect(autoPollingSvc.setSuppression).toHaveBeenCalledWith(true);
    });

    it('should suppress polling when open Save Search dialogue pane', () => {
      const router = TestBed.get(Router);
      const autoPollingSvc = TestBed.get(AutoPollingService);
      const saveSearchSvc = TestBed.get(SaveSearchService);
      spyOn(router, 'navigate');
      spyOn(router, 'navigateByUrl');
      spyOn(autoPollingSvc, 'setSuppression');
      spyOn(saveSearchSvc, 'setCurrentQueryBuilderAndTableColumns');

      component.showSaveSearch();

      expect(autoPollingSvc.setSuppression).toHaveBeenCalledWith(true);
    });

    it('should restore the polling supression on bulk status update (other scenario of deselecting alerts)', () => {
      const autoPollingSvc = TestBed.get(AutoPollingService);
      spyOn(autoPollingSvc, 'setSuppression');

      component.updateSelectedAlertStatus('fakeState');

      expect(autoPollingSvc.setSuppression).toHaveBeenCalledWith(false);
    });

    it('should restore the polling supression when returning from a subroute', fakeAsync(() => {
      const autoPollingSvc = TestBed.get(AutoPollingService);
      spyOn(autoPollingSvc, 'setSuppression');

      autoPollingSvc.getIsPollingActive = () => false;
      fixture.ngZone.run(() => {
        TestBed.get(Router).navigate(['/alerts-list']);
      });

      expect(autoPollingSvc.setSuppression).not.toHaveBeenCalled();

      autoPollingSvc.getIsPollingActive = () => true;
      fixture.ngZone.run(() => {
        TestBed.get(Router).navigate(['/alerts-list']);
      });

      expect(autoPollingSvc.setSuppression).toHaveBeenCalledWith(false);
    }));
  });

  describe('search', () => {
    it('should fire onSearch if in manual search mode', () => {
      const testQuery = 'test';
      spyOn(queryBuilder, 'setFilteringMode').and.returnValue(FilteringMode.BUILDER);
      spyOn(queryBuilder, 'setSearch');
      spyOn(component, 'search');
      component.toggleQueryBuilderMode();
      fixture.detectChanges();
      component.onSearch(testQuery);
      expect(queryBuilder.setSearch).toHaveBeenCalledWith(testQuery);
    });

    it('should show notification on http error', fakeAsync(() => {
      const fakeDialogService = TestBed.get(DialogService);

      spyOn(searchService, 'search').and.returnValue(throwError(new RestError()));
      fakeDialogService.launchDialog = () => {};
      spyOn(fakeDialogService, 'launchDialog');

      component.search();

      expect(fakeDialogService.launchDialog).toHaveBeenCalledWith('Server were unable to apply query string.', DialogType.Error);
    }));

    it('should save current search', () => {
      const saveSearchService = TestBed.get(SaveSearchService);
      const search = new SaveSearch();

      spyOn(saveSearchService, 'saveAsRecentSearches').and.callThrough();
      spyOn(queryBuilder, 'query').and.returnValue('test');
      component.saveCurrentSearch(search);
      expect(saveSearchService.saveAsRecentSearches).toHaveBeenCalledWith(search);
    });
  });

 describe('actions', () => {
    const testAlert: Alert = {
      id: '123',
      score: 123,
      source: new AlertSource(),
      status: 'TEST',
      index: 'test'
    }

    it('should prevent a user from selecting a new alert state if disabled', () => {
      const updateService = TestBed.get(UpdateService);
      spyOn(updateService, 'updateAlertState')
      spyOn(component, 'preventDropdownOptionIfDisabled');
      const actionsButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="dropdown-menu-button"');
      actionsButton.click();
      const openButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="action-open-button"');
      expect(openButton.classList).toContain('disabled');
      openButton.dispatchEvent(new Event('click'));
      expect(component.preventDropdownOptionIfDisabled).toHaveBeenCalled();
      expect(updateService.updateAlertState).not.toHaveBeenCalled();
    });


    it('should update the alert state to open when selected', () => {
      component.selectedAlerts.push(testAlert);
      fixture.detectChanges();
      const updateService = TestBed.get(UpdateService);
      spyOn(updateService, 'updateAlertState').and.returnValue(of({}));
      spyOn(component, 'processOpen').and.callThrough();
      spyOn(component, 'updateSelectedAlertStatus');
      const actionsButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="dropdown-menu-button"');
      actionsButton.click();
      const openButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="action-open-button"');
      openButton.dispatchEvent(new Event('click'));
      fixture.detectChanges();
      expect(component.processOpen).toHaveBeenCalled();
      expect(updateService.updateAlertState).toHaveBeenCalledWith(component.selectedAlerts, 'OPEN', false);
    });

    it('should update the alert state to dismiss when selected', () => {
      component.selectedAlerts.push(testAlert);
      fixture.detectChanges();
      const updateService = TestBed.get(UpdateService);
      spyOn(updateService, 'updateAlertState').and.returnValue(of());
      spyOn(component, 'processDismiss').and.callThrough();
      spyOn(component, 'updateSelectedAlertStatus');
      const actionsButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="dropdown-menu-button"');
      actionsButton.click();
      const dismissButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="action-dismiss-button"');
      dismissButton.dispatchEvent(new Event('click'));
      fixture.detectChanges();
      expect(component.processDismiss).toHaveBeenCalled();
      expect(updateService.updateAlertState).toHaveBeenCalledWith(component.selectedAlerts, 'DISMISS', false);
    });

    it('should update the alert state to escalate when selected', () => {
      component.selectedAlerts.push(testAlert);
      fixture.detectChanges();
      const updateService = TestBed.get(UpdateService);
      spyOn(updateService, 'updateAlertState').and.returnValue(of());
      spyOn(component, 'processEscalate').and.callThrough();
      spyOn(component, 'updateSelectedAlertStatus');
      const actionsButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="dropdown-menu-button"');
      actionsButton.click();
      const escalateButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="action-escalate-button"');
      escalateButton.dispatchEvent(new Event('click'));
      fixture.detectChanges();
      expect(component.processEscalate).toHaveBeenCalled();
      expect(updateService.updateAlertState).toHaveBeenCalledWith(component.selectedAlerts, 'ESCALATE', false);
    });

    it('should update the alert state to resolve when selected', () => {
      component.selectedAlerts.push(testAlert);
      fixture.detectChanges();
      const updateService = TestBed.get(UpdateService);
      spyOn(updateService, 'updateAlertState').and.returnValue(of());
      spyOn(component, 'processResolve').and.callThrough();
      spyOn(component, 'updateSelectedAlertStatus');
      const actionsButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="dropdown-menu-button"');
      actionsButton.click();
      const resolveButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="action-resolve-button"');
      resolveButton.dispatchEvent(new Event('click'));
      fixture.detectChanges();
      expect(component.processResolve).toHaveBeenCalled();
      expect(updateService.updateAlertState).toHaveBeenCalledWith(component.selectedAlerts, 'RESOLVE', false);
    });

    it('should add to alert when selected', () => {
      component.selectedAlerts.push(testAlert);
      fixture.detectChanges();
      const metaAlertService = TestBed.get(MetaAlertService);
      const router = TestBed.get(Router);
      spyOn(component, 'processAddToAlert').and.callThrough();
      spyOn(component, 'updateSelectedAlertStatus');
      spyOn(router, 'navigateByUrl');
      const actionsButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="dropdown-menu-button"');
      actionsButton.click();
      const processAddToAlertButton = fixture.debugElement.nativeElement.querySelector('[data-qe-id="action-add-to-alert-button"');
      processAddToAlertButton.dispatchEvent(new Event('click'));
      fixture.detectChanges();
      expect(component.processAddToAlert).toHaveBeenCalled();
      expect(metaAlertService.selectedAlerts).toEqual(component.selectedAlerts);
      expect(router.navigateByUrl).toHaveBeenCalledWith('/alerts-list(dialog:add-to-meta-alert)');
    });
  });

  describe('table configuration', () => {
    it('should reconfigure rows when value is emitted to configRowsChange', () => {
      const configureTableService = TestBed.get(ConfigureTableService);
      spyOn(component, 'updatePollingInterval');
      spyOn(component, 'search');
      spyOn(configureTableService, 'saveTableMetaData').and.callThrough();
      component.onConfigRowsChange({
        values: {
          pageSize: 10,
          refreshInterval: 1000
        },
        triggerQuery: true
      });

      expect(component.tableMetaData.size).toEqual(10);
      expect(configureTableService.saveTableMetaData).toHaveBeenCalledWith(component.tableMetaData);
      expect(component.search).toHaveBeenCalled();
    });
  });

  describe('save/load manual query search', () => {

    it('should switch to manual mode if the saved search is manual', () => {

      const saveSearchSvc = TestBed.get(SaveSearchService);
      const savedSearch = new SaveSearch();
      savedSearch.isManual = true;
      savedSearch.searchRequest = new SearchRequest();
      savedSearch.searchRequest.query = 'foo:bar';
      savedSearch.filters = [];

      saveSearchSvc.loadSavedSearch$ = of(savedSearch);

      const spySetFilteringMode = spyOn(component.queryBuilder, 'setFilteringMode');
      const spySetManualQuery = spyOn(component.queryBuilder, 'setManualQuery');

      component.addLoadSavedSearchListener();

      expect(spySetFilteringMode).toHaveBeenCalledTimes(1);
      expect(spySetFilteringMode).toHaveBeenCalledWith(FilteringMode.MANUAL);

      expect(spySetManualQuery).toHaveBeenCalledWith('foo:bar');
    });

    it('should switch to builder mode if the saved search is not manual', () => {
      const saveSearchSvc = TestBed.get(SaveSearchService);
      const savedSearch = new SaveSearch();
      savedSearch.isManual = false;
      savedSearch.searchRequest = new SearchRequest();
      savedSearch.searchRequest.query = 'foo:bar';
      savedSearch.filters = [];

      saveSearchSvc.loadSavedSearch$ = of(savedSearch);

      const spySetFilteringMode = spyOn(component.queryBuilder, 'setFilteringMode');
      const spySetManualQuery = spyOn(component.queryBuilder, 'setManualQuery');

      component.addLoadSavedSearchListener();

      expect(spySetFilteringMode).toHaveBeenCalledTimes(1);
      expect(spySetFilteringMode).toHaveBeenCalledWith(FilteringMode.BUILDER);

      expect(spySetManualQuery).not.toHaveBeenCalledWith('foo:bar');
    });

    it('should save the search filter mode (manual)', (done) => {
      const saveSearchSvc = TestBed.get(SaveSearchService);

      saveSearchSvc.saveAsRecentSearches = (saveSearch) => {
        expect(saveSearch.isManual).toBe(true);
        expect(saveSearch.searchRequest.query).toBe('foo:bar');
        done();
        return of(null);
      };

      component.queryBuilder.getFilteringMode = () => FilteringMode.MANUAL;
      component.queryBuilder.query = 'foo:bar';
      component.saveCurrentSearch();
    });

    it('should save the search filter mode (builder)', (done) => {
      const saveSearchSvc = TestBed.get(SaveSearchService);

      saveSearchSvc.saveAsRecentSearches = (saveSearch) => {
        expect(saveSearch.isManual).toBe(false);
        expect(saveSearch.searchRequest.query).toBe('');
        done();
        return of(null);
      };

      component.queryBuilder.getFilteringMode = () => FilteringMode.BUILDER;
      component.queryBuilder.query = 'foo:bar';
      component.saveCurrentSearch();
    });
  });
});
