mysticPrg/catch-light

View on GitHub
src/components/light/Light.test.js

Summary

Maintainability
C
1 day
Test Coverage
import React from 'react';
import { shallow, mount } from 'enzyme';

import Light from './Light';
import { getFakeKey, getRandomColor } from '~/utils/common';

jest.useRealTimers();

const timer_margin = 500;

describe('Light', () => {
    it('should be rendered without crash', () => {
        expect(() => {
            shallow(<Light/>);
        }).not.toThrow();
    });

    it('should be clickabled', () => {
        const onClick = jasmine.createSpy();
        const id = getFakeKey();

        const light = shallow(
            <Light
                id={id}
                onClick={onClick}
            />
        );

        light.simulate('click');

        expect(onClick).toHaveBeenCalledWith(id);
    });

    it.concurrent('should be animated with given fps', () => {
        const fps = 20;
        const onAnimate = jasmine.createSpy();

        const light = mount(
            <Light 
                fps={fps}
                onAnimate={onAnimate}
            />
        );

        const promise = new Promise(resolve => setTimeout(resolve, 1000 + timer_margin));

        promise.then(() => {
            expect(onAnimate.calls.count()).toBeGreaterThanOrEqual(fps);
            light.unmount();
        });

        return promise;
    });

    it.concurrent('more than 30 must work independently', () => {
        const count = 30;
        const fps = 30;
        const onAnimate = jasmine.createSpy();

        const lightArr = [];
        for (let i=0 ; i<count ; i++ ) {
            lightArr.push(mount(
                <Light
                    onAnimate={onAnimate}
                    fps={fps}
                />
            ));
        }

        const promise = new Promise(resolve => setTimeout(resolve, 1000 + timer_margin));
        promise.then(() => {
            expect(onAnimate.calls.count()).toBeGreaterThanOrEqual(fps*count);
            for (let i=0 ; i<count ; i++ ) {
                lightArr[i].unmount();
            }
        });

        return promise;
    });
});

describe('Light position', () => {

    beforeEach(() => {
        Light.displayName = Math.random().toString();
    });

    it('should have x and y values that are randomly initialized within a given range', () => {
        // given
        const x_min = 0;
        const x_max = 1000;
        const y_min = 0;
        const y_max = 1000;

        // when
        const light = shallow(
            <Light
                x-min={x_min}
                x-max={x_max}
                y-min={y_min}
                y-max={y_max}
            />
        );

        //then
        const style = light.find('[data-test="light"]').props().style;
        const px_filter = /([0-9]*)px/i;
        const x_pos = parseInt(px_filter.exec(style.left)[1], 10);
        const y_pos = parseInt(px_filter.exec(style.top)[1], 10);

        expect(x_pos).toBeGreaterThanOrEqual(x_min);
        expect(x_pos).toBeLessThan(x_max);

        expect(y_pos).toBeGreaterThanOrEqual(y_min);
        expect(y_pos).toBeLessThan(y_max);
    });

    it('x-min should be less than x-max', () => {
        const min = 10;
        const max = 5;
        
        const errSpy = spyOn(console, 'error');
        shallow(
            <Light
                x-min={min}
                x-max={max}
            />
        );
        expect(errSpy).toHaveBeenCalled();
    });

    it('y-min should be less than y-max', () => {
        const min = 10;
        const max = 5;
        
        const errSpy = spyOn(console, 'error');
        shallow(
            <Light
                y-min={min}
                y-max={max}
            />
        );
        expect(errSpy).toHaveBeenCalled();
    });

    it('x and y range is 0 to 1000', () => {
        const min = 0;
        const max = 1000;

        const errSpy = spyOn(console, 'error');
        for ( let i=min ; i<max ; i++) {
            shallow(
                <Light
                    x-min={i}
                    x-max={i}
                    y-min={i}
                    y-max={i}
                />
            );
        }
        expect(errSpy).not.toHaveBeenCalled();

        const error_min = -1;
        const error_max = 1001;
        errSpy.calls.reset();
        shallow(
            <Light
                x-min={error_min}
                x-max={error_max}
                y-min={error_min}
                y-max={error_max}
            />
        );
        expect(errSpy).toHaveBeenCalled();

        errSpy.calls.reset();
        const not_number_value = 'not a number';
        shallow(
            <Light
                x-min={not_number_value}
                x-max={not_number_value}
                y-min={not_number_value}
                y-max={not_number_value}
            />
        );
        expect(errSpy).toHaveBeenCalled();
    });
    
    it.concurrent('Each time animate is called, it must be close to the target', () => {
        const target = {
            x: 100,
            y: 100
        };
        const min = 0;
        const max = 500;
        const repeatCount = 30;
        let light, prevDist, curDist;
        
        const getPos = () => {
            const style = light.find('[data-test="light"]').props().style;
            const px_filter = /([0-9.]*)px/i;
            const x = parseInt(px_filter.exec(style.left)[1], 10);
            const y = parseInt(px_filter.exec(style.top)[1], 10);
            return {x, y};
        };
        const getDist = (target, pos = getPos()) => ({
            x: Math.abs(target.x*target.x - pos.x*pos.x),
            y: Math.abs(target.y*target.y - pos.y*pos.y)
        });

        let repeat = 0;
        let onAnimate = null;
        const promise = new Promise(resolve => {
            onAnimate = () => {
                if ( repeat < repeatCount ) {
                    curDist = getDist(target);
                    expect(curDist.x).toBeLessThan(prevDist.x);
                    expect(curDist.y).toBeLessThan(prevDist.y);
                    repeat++;
                } else {
                    resolve();
                }
            };
        });
        
        light = mount(
            <Light
                onAnimate={onAnimate}
                target-x={target.x}
                target-y={target.y}
                x-min={min}
                y-min={min}
                x-max={max}
                y-max={max}
            />
        );

        prevDist = getDist(target);
        curDist = null;

        promise.then(() => {
            curDist = getDist(target);
            expect(curDist.x).toBeLessThan(prevDist.x);
            expect(curDist.y).toBeLessThan(prevDist.y);

            light.unmount();
        });

        return promise;
    });
});

describe('Light shape', () => {
    it('should be show given color', () => {
        const color = getRandomColor();
        const light = shallow(
            <Light
                color={color}
            />
        );

        const result = light.find('[data-test="light"]').props().style['backgroundColor'];
        expect(result).toEqual(color);
    });

    it('should be show given size', () => {
        const size = 100;
        const light = shallow(
            <Light
                size={size}
            />
        );

        const style = light.find('[data-test="light"]').props().style;
        const width = style.width;
        const height = style.height;

        expect(width).toEqual(`${size}px`);
        expect(height).toEqual(`${size}px`);
    });

    it.concurrent('should be show given alpha with animate', () => {
        const alpha = 0.5;
        let loopCount = 0;
        const onAnimate = jasmine.createSpy(() => loopCount++);
        const light = mount(
            <Light
                alpha={alpha}
                onAnimate={onAnimate}
            />
        );

        let style = light.find('[data-test="light"]').props().style;
        let result = style.opacity;
        expect(onAnimate).not.toBeCalled();
        expect(result).toEqual(0);

        return new Promise(resolve => {
            setTimeout(resolve, 1000 + timer_margin);
        }).then(() => {
            expect(onAnimate).toBeCalled();
            style = light.find('[data-test="light"]').props().style;
            result = style.opacity;
            expect(result).toEqual(alpha);

            light.unmount();
        });
    });

    it('should be show circle shape', () => {
        const light = shallow(<Light/>);

        const style = light.find('[data-test="light"]').props().style;
        const result = style.borderRadius;

        expect(result).toEqual('50%');
    });

    it('mouse cursor should be change to hand when hover', () => {
        const light = shallow(<Light/>);

        const style = light.find('[data-test="light"]').props().style;
        const result = style.cursor;

        expect(result).toEqual('pointer');
    });
});