Ks89/angular-modal-gallery

View on GitHub
projects/ks89/angular-modal-gallery/src/lib/components/dots/dots.component.spec.ts

Summary

Maintainability
F
1 wk
Test Coverage
/*
 * Copyright (c) 2017-2024 Stefano Cappa (Ks89)
 *
 * Licensed 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 { ComponentFixture, TestBed } from '@angular/core/testing';

import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';

import { DotsComponent } from './dots.component';
import { AccessibilityConfig } from '../../model/accessibility.interface';
import { DotsConfig } from '../../model/dots-config.interface';
import { KS_DEFAULT_ACCESSIBILITY_CONFIG } from '../../components/accessibility-default';
import { InternalLibImage } from '../../model/image-internal.class';
import { ConfigService } from '../../services/config.service';
import { AccessibleComponent } from '../accessible.component';

let comp: DotsComponent;
let fixture: ComponentFixture<DotsComponent>;
const GALLERY_ID = 1;

const CUSTOM_ACCESSIBILITY: AccessibilityConfig = Object.assign({}, KS_DEFAULT_ACCESSIBILITY_CONFIG);
CUSTOM_ACCESSIBILITY.dotsContainerTitle = 'custom dotsContainerTitle';
CUSTOM_ACCESSIBILITY.dotsContainerAriaLabel = 'custom dotsContainerAriaLabel';

const DOTS_CONFIG_VISIBLE: DotsConfig = {visible: true};
const DOTS_CONFIG_HIDDEN: DotsConfig = {visible: false};

const IMAGES: InternalLibImage[] = [
  new InternalLibImage(0, {
    // modal
    img: '../assets/images/gallery/img1.jpg',
    extUrl: 'http://www.google.com'
  }),
  new InternalLibImage(1, {
    // modal
    img: '../assets/images/gallery/img2.png',
    description: 'Description 2'
  }),
  new InternalLibImage(
    2,
    {
      // modal
      img: '../assets/images/gallery/img3.jpg',
      description: 'Description 3',
      extUrl: 'http://www.google.com'
    },
    {
      // plain
      img: '../assets/images/gallery/thumbs/img3.png',
      title: 'custom title 2',
      alt: 'custom alt 2',
      ariaLabel: 'arial label 2'
    }
  ),
  new InternalLibImage(3, {
    // modal
    img: '../assets/images/gallery/img4.jpg',
    description: 'Description 4',
    extUrl: 'http://www.google.com'
  }),
  new InternalLibImage(
    4,
    {
      // modal
      img: '../assets/images/gallery/img5.jpg'
    },
    {
      // plain
      img: '../assets/images/gallery/thumbs/img5.jpg'
    }
  )
];

function initTestBed(): void {
  TestBed.configureTestingModule({
    declarations: [DotsComponent, AccessibleComponent]
  }).overrideComponent(DotsComponent, {
    set: {
      providers: [
        {
          provide: ConfigService,
          useClass: ConfigService
        }
      ]
    }
  });
}

describe('DotsComponent', () => {
  beforeEach(() => {
    initTestBed();
    fixture = TestBed.createComponent(DotsComponent);
    comp = fixture.componentInstance;
  });

  it('should instantiate it', () => expect(comp).not.toBeNull());

  describe('---YES---', () => {

    it(`should display dots (first one is active) based of the number of input images`, () => {
      const activeDotIndex = 0;
      const configService = fixture.debugElement.injector.get(ConfigService);
      configService.setConfig(GALLERY_ID, {
        dotsConfig: DOTS_CONFIG_VISIBLE,
        accessibilityConfig: KS_DEFAULT_ACCESSIBILITY_CONFIG
      });
      comp.id = GALLERY_ID;
      comp.currentImage = IMAGES[activeDotIndex];
      comp.images = IMAGES;
      comp.ngOnInit();
      fixture.detectChanges();

      const element: DebugElement = fixture.debugElement;

      const dotsContainer: DebugElement = element.query(By.css('nav.dots-container'));
      expect(dotsContainer.name).toBe('nav');
      expect(dotsContainer.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerAriaLabel);
      expect(dotsContainer.properties.title).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerTitle);

      const dots: DebugElement[] = dotsContainer.children;
      expect(dots.length).toBe(IMAGES.length);

      dots.forEach((dot: DebugElement, index: number) => {
        expect(dot.name).toBe('div');
        expect(dot.attributes.role).toBe('navigation');
        expect(dot.properties.tabIndex).toBe(0);
        if (index === activeDotIndex) {
          // I don't know why, but with dot.attributes.class I can't see 'active'. In this way it's working!
          expect(dot.classes).toEqual({inside: true, dot: true, active: true});
        } else {
          expect(dot.attributes.class).toBe('inside dot');
          // or like above: expect(dot.classes).toEqual({'inside': true, 'dot': true});
        }
        expect(dot.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotAriaLabel + ' ' + (index + 1));
      });
    });

    it(`should display dots (first one is active), because by default dotsConfig are visible`, () => {
      const activeDotIndex = 0;
      const configService = fixture.debugElement.injector.get(ConfigService);
      configService.setConfig(GALLERY_ID, {
        dotsConfig: undefined, // or null, or something not valid
        accessibilityConfig: KS_DEFAULT_ACCESSIBILITY_CONFIG
      });
      comp.id = GALLERY_ID;
      comp.currentImage = IMAGES[activeDotIndex];
      comp.images = IMAGES;
      comp.ngOnInit();
      fixture.detectChanges();

      const element: DebugElement = fixture.debugElement;

      const dotsContainer: DebugElement = element.query(By.css('nav.dots-container'));
      expect(dotsContainer.name).toBe('nav');
      expect(dotsContainer.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerAriaLabel);
      expect(dotsContainer.properties.title).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerTitle);

      const dots: DebugElement[] = dotsContainer.children;
      expect(dots.length).toBe(IMAGES.length);

      dots.forEach((dot: DebugElement, index: number) => {
        expect(dot.name).toBe('div');
        expect(dot.attributes.role).toBe('navigation');
        expect(dot.properties.tabIndex).toBe(0);

        if (index === activeDotIndex) {
          // I don't know why, but with dot.attributes.class I can't see 'active'. In this way it's working!
          expect(dot.classes).toEqual({inside: true, dot: true, active: true});
        } else {
          expect(dot.attributes.class).toBe('inside dot');
          // or like above: expect(dot.classes).toEqual({'inside': true, 'dot': true});
        }
        expect(dot.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotAriaLabel + ' ' + (index + 1));
      });
    });

    it(`should display dots (first one is active) with custom accessibility`, () => {
      const activeDotIndex = 0;
      const configService = fixture.debugElement.injector.get(ConfigService);
      configService.setConfig(GALLERY_ID, {
        dotsConfig: DOTS_CONFIG_VISIBLE,
        accessibilityConfig: CUSTOM_ACCESSIBILITY
      });
      comp.id = GALLERY_ID;
      comp.currentImage = IMAGES[activeDotIndex];
      comp.images = IMAGES;
      comp.ngOnInit();
      fixture.detectChanges();

      const element: DebugElement = fixture.debugElement;

      const dotsContainer: DebugElement = element.query(By.css('nav.dots-container'));
      expect(dotsContainer.name).toBe('nav');
      expect(dotsContainer.attributes['aria-label']).toBe(CUSTOM_ACCESSIBILITY.dotsContainerAriaLabel);
      expect(dotsContainer.properties.title).toBe(CUSTOM_ACCESSIBILITY.dotsContainerTitle);

      const dots: DebugElement[] = dotsContainer.children;
      expect(dots.length).toBe(IMAGES.length);

      dots.forEach((dot: DebugElement, index: number) => {
        expect(dot.name).toBe('div');
        expect(dot.attributes.role).toBe('navigation');
        expect(dot.properties.tabIndex).toBe(0);

        if (index === activeDotIndex) {
          // I don't know why, but with dot.attributes.class I can't see 'active'. In this way it's working!
          expect(dot.classes).toEqual({inside: true, dot: true, active: true});
        } else {
          expect(dot.attributes.class).toBe('inside dot');
          // or like above: expect(dot.classes).toEqual({'inside': true, 'dot': true});
        }
        expect(dot.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotAriaLabel + ' ' + (index + 1));
      });
    });

    it(`should display dots and click on one of themem`, () => {
      const indexToClick = 1;
      const activeDotIndex = 0;
      const configService = fixture.debugElement.injector.get(ConfigService);
      configService.setConfig(GALLERY_ID, {
        dotsConfig: DOTS_CONFIG_VISIBLE,
        accessibilityConfig: KS_DEFAULT_ACCESSIBILITY_CONFIG
      });
      comp.id = GALLERY_ID;
      comp.currentImage = IMAGES[activeDotIndex];
      comp.images = IMAGES;
      comp.ngOnInit();
      fixture.detectChanges();

      const element: DebugElement = fixture.debugElement;

      comp.clickDot.subscribe((index: number) => {
        expect(index).toBe(indexToClick);
      }, () => fail('after a click I should receive a clickDot event'));

      const dotsContainer: DebugElement = element.query(By.css('nav.dots-container'));
      expect(dotsContainer).not.toBeNull();
      const dots: DebugElement[] = dotsContainer.children;
      expect(dots.length).toBe(IMAGES.length);

      // clicks on a dot
      dots[1].nativeElement.click();
    });
  });

  describe('---NO---', () => {

    it(`shouldn't display dots, because visibility is false.`, () => {
      const configService = fixture.debugElement.injector.get(ConfigService);
      configService.setConfig(GALLERY_ID, {
        dotsConfig: DOTS_CONFIG_VISIBLE,
        accessibilityConfig: KS_DEFAULT_ACCESSIBILITY_CONFIG
      });
      comp.id = GALLERY_ID;
      comp.ngOnInit();
      fixture.detectChanges();

      const element: DebugElement = fixture.debugElement;

      const dotsContainer: DebugElement = element.query(By.css('nav.dots-container'));
      expect(dotsContainer.name).toBe('nav');
      expect(dotsContainer.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerAriaLabel);
      expect(dotsContainer.properties.title).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerTitle);

      const dots: DebugElement[] = element.queryAll(By.css('div.inside.dot'));
      expect(dots.length).toBe(0);
    });

    it(`shouldn't display dots, because the array of images as input is empty`, () => {
      const configService = fixture.debugElement.injector.get(ConfigService);
      configService.setConfig(GALLERY_ID, {
        accessibilityConfig: KS_DEFAULT_ACCESSIBILITY_CONFIG
      });
      comp.id = GALLERY_ID;
      comp.currentImage = undefined;
      comp.images = [];
      comp.ngOnInit();
      fixture.detectChanges();

      const element: DebugElement = fixture.debugElement;

      const dotsContainer: DebugElement = element.query(By.css('nav.dots-container'));
      expect(dotsContainer.name).toBe('nav');
      expect(dotsContainer.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerAriaLabel);
      expect(dotsContainer.properties.title).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerTitle);

      const dots: DebugElement[] = dotsContainer.children;
      expect(dots.length).toBe(0);
    });

    it(`shouldn't display dots, because the array of images as input is not valid`, () => {
      const configService = fixture.debugElement.injector.get(ConfigService);
      configService.setConfig(GALLERY_ID, {
        accessibilityConfig: KS_DEFAULT_ACCESSIBILITY_CONFIG
      });
      comp.id = GALLERY_ID;
      comp.currentImage = undefined;
      comp.images = undefined;
      comp.ngOnInit();
      fixture.detectChanges();

      const element: DebugElement = fixture.debugElement;

      const dotsContainer: DebugElement = element.query(By.css('nav.dots-container'));
      expect(dotsContainer.name).toBe('nav');
      expect(dotsContainer.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerAriaLabel);
      expect(dotsContainer.properties.title).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerTitle);

      const dots: DebugElement[] = dotsContainer.children;
      expect(dots.length).toBe(0);
    });

    it(`shouldn't display active dot when the currentImage is invalid, because 'isActive' method throws a managed error and return false`, () => {
      const configService = fixture.debugElement.injector.get(ConfigService);
      configService.setConfig(GALLERY_ID, {
        accessibilityConfig: KS_DEFAULT_ACCESSIBILITY_CONFIG
      });
      comp.id = GALLERY_ID;
      // create a fake image not available in comp.images array
      comp.currentImage = new InternalLibImage(99, IMAGES[0].modal);
      comp.images = IMAGES;
      comp.ngOnInit();
      fixture.detectChanges();

      const element: DebugElement = fixture.debugElement;

      const dotsContainer: DebugElement = element.query(By.css('nav.dots-container'));
      expect(dotsContainer.name).toBe('nav');
      expect(dotsContainer.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerAriaLabel);
      expect(dotsContainer.properties.title).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotsContainerTitle);

      const dots: DebugElement[] = dotsContainer.children;
      expect(dots.length).toBe(IMAGES.length);

      // all dots are NOT active, bat simply 'inside dot'
      dots.forEach((dot: DebugElement, index: number) => {
        expect(dot.name).toBe('div');
        expect(dot.attributes.role).toBe('navigation');
        expect(dot.properties.tabIndex).toBe(0);
        expect(dot.attributes.class).toBe('inside dot');
        expect(dot.attributes['aria-label']).toBe(KS_DEFAULT_ACCESSIBILITY_CONFIG.dotAriaLabel + ' ' + (index + 1));
      });
    });
  });
});