Как использовать mockDOMSource для тестирования потока действий в Cycle.js?

Я понимаю, что, вероятно, есть лучший способ использовать цикл/время, но я просто пытаюсь понять основы. Почему-то мой поток action$ не работает; Я пытался создать несколько фиктивных домов, используя xs.periodic. Тестовая среда — мокко.

import 'mocha';
import {expect} from 'chai';
import xs from 'xstream';
import Stream from 'xstream';
import {mockDOMSource, DOMSource} from '@cycle/dom';
import {HTTPSource} from '@cycle/http';
import XStreamAdapter from '@cycle/xstream-adapter';

export interface Props {
  displayAbs: boolean
}

export interface ISources {
  DOM: DOMSource;
  http: HTTPSource;
}

function testIntent(sources: ISources):Stream<Props> {
  return  xs.merge<Props>(
      sources.DOM
          .select('.absShow').events('click')
          .mapTo( { displayAbs: true } ),
      sources.DOM
          .select('.absHide').events('click')
          .mapTo( { displayAbs: false } )
  ).startWith( {displayAbs: false } );
}

describe( 'Test', ()=>{

  describe( 'intent()', ()=>{

    it('should change on click to shows and hides', () => {
      let listenerGotEnd = false;

      const mDOM$: Stream<DOMSource> = xs.periodic(1000).take(6).map(ii => {
        if (ii % 2 == 0) {
          return mockDOMSource(XStreamAdapter, {
            '.absShow': {'click': xs.of({target: {}})}
          })
        }
        else {
          return mockDOMSource(XStreamAdapter, {
            '.absHide': {'click': xs.of({target: {}})}
          })
        }
      });

      const action$ = mDOM$.map(mDOM => testIntent({
        DOM: mDOM,
        http: {} as HTTPSource,
      })).flatten();


      action$.addListener({
        next: (x) => {
          console.log("x is " + x.displayAbs);
        },
        error: (err) => {
          console.log("error is:" + err);
            throw err;
        },
        complete: () => { listenerGotEnd = true; }
      });
      expect(listenerGotEnd).to.equal(true);
    });

  });/* end of describe intent */

});

person bbarker    schedule 17.02.2017    source источник


Ответы (1)


Основная причина, по которой тест не выполняется, заключается в том, что он асинхронный, поэтому в мокко нам нужно принять обратный вызов done, а затем вызвать его, когда наш тест будет завершен.

Без использования @cycle/time я бы написал этот тест следующим образом:

import 'mocha';
import {expect} from 'chai';
import xs, {Stream} from 'xstream';
import {mockDOMSource, DOMSource} from '@cycle/dom';
import XStreamAdapter from '@cycle/xstream-adapter';

export interface Props {
  displayAbs: boolean
}

export interface ISources {
  DOM: DOMSource;
}

function testIntent(sources: ISources):Stream<Props> {
  return  xs.merge<Props>(
      sources.DOM
          .select('.absShow').events('click')
          .mapTo( { displayAbs: true } ),
      sources.DOM
          .select('.absHide').events('click')
          .mapTo( { displayAbs: false } )
  ).startWith( {displayAbs: false } );
}

describe('Test', () => {
  describe('intent()', () => {
    it('should change on click to shows and hides', (done) => {
      const show$ = xs.create();
      const hide$ = xs.create();

      const DOM = mockDOMSource(XStreamAdapter, {
        '.absShow': {
          'click': show$
        },

        '.absHide': {
          'click': hide$
        }
      });

      const intent$ = testIntent({DOM});

      const expectedValues = [
        {displayAbs: false},
        {displayAbs: true},
        {displayAbs: false},
      ]

      intent$.take(expectedValues.length).addListener({
        next: (x) => {
          expect(x).to.deep.equal(expectedValues.shift());
        },
        error: done,
        complete: done
      });

      show$.shamefullySendNext({});
      hide$.shamefullySendNext({});
    });
  });
});

Этот тест выполняется за 11 мс, что немного быстрее, чем при использовании xs.periodic(1000).take(6).

Для сравнения, вот как я бы написал это с @cycle/time:

import {mockTimeSource} from '@cycle/time'

describe('Test', () => {
  describe('intent()', () => {
    it('should change on click to shows and hides', (done) => {
      const Time = mockTimeSource();

      const show$     = Time.diagram('---x-----');
      const hide$     = Time.diagram('------x--');
      const expected$ = Time.diagram('f--t--f--', {f: false, t: true});

      const DOM = mockDOMSource({
        '.absShow': {
          'click': show$
        },

        '.absHide': {
          'click': hide$
        }
      });

      const intent$ = testIntent({DOM}).map(intent => intent.displayAbs);

      Time.assertEqual(intent$, expected$);

      Time.run(done);
    });
  });
});

Первая версия — это фактически то, что @cycle/time делает для вас под капотом, это просто немного более приятный способ написания. Также приятно иметь более качественные сообщения об ошибках.

person Widdershin    schedule 17.02.2017