A guide on utilizing mockDOMSource for testing a sequence of actions within Cycle.js

Although I am aware that there might be a more efficient way using cycle/time, my main focus is to grasp the fundamentals. For some reason, my action$ stream does not appear to be functioning; I have attempted to create multiple mock doms using xs.periodic. The testing framework being used is mocha.

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 */

});

Answer №1

One main factor causing the test to not run properly is its asynchronous nature, requiring us in mocha to use the done callback and invoke it once the test is complete.

In the absence of @cycle/time, below is how I would approach writing this particular test:

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({});
    });
  });
});

This specific test completes in just 11ms, a significant improvement compared to using xs.periodic(1000).take(6)

On a comparative note, here's how I would tackle it with @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);
    });
  });
});

The initial version essentially mirrors what @cycle/time accomplishes behind the scenes, offering a more streamlined approach. Moreover, it enhances error handling capabilities.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to align an element next to another using id or class?

I am looking to align the search element next to either "Media Heading 1" or "Media Heading 2" based on their id/class. For instance: Assume I have an element with the class of "media-item-1" and I aim to position the search div alongside that element us ...

Converting a Javascript array to an NSArray in Xcode

As a complete beginner to Xcode and programming in general, I recently built an app using javascript and html and integrated it into Xcode. Currently, my main focus is on extracting multiple arrays from the html/javascript file and exporting them as a csv ...

Getting the values of several labels using a class name - a comprehensive guide

Is there a way to retrieve the values of labels with the "timeAuction" class? I'm currently working on a JavaScript function that will target each label with the class name timeAuction and adjust its value. The number of labels with this class can va ...

Tips for displaying indentations on Tube Geometry using THREE.js

Currently, I have a project where I am tasked with displaying dents on a pipeline in a 3D format. To create the pipeline, I utilized THREE.js's tube geometry which is illustrated below: <!DOCTYPE html> <html lang="en"> <head> ...

Switch out the ajax data in the input field

Is there a way to update the value in a text box using Ajax? Below is my code snippet: <input type="text" id="category_name" name="category_name" value="<?php if(isset($compName)) { echo ucfirst($compName); ...

Exploring the DOM within the Vue component's Mounted lifecycle hook

Is there a way to access an element from within the mounted function in a VueJS instance? I attempted to do so using the code below, but it returned an error stating that the element is undefined. However, upon checking the DOM, I can see that the element ...

ReactJS - How to prevent infinite loops when using the setState method inside the onChange

My attempt at creating a component using codemirror seems to be failing. When I try to update the state of my main component, the application breaks the browser. My research indicates that there might be an infinite loop causing this issue, especially when ...

The saving function of the Jquery camera is not working properly and does not store the

I seem to be having an issue when using the jquery camera in "save" mode. Despite trying to call the url in "webcam.save" manually, it doesn't seem to have any effect. It appears that the jquery camera plugin may not be functioning as intended. Any su ...

The functions .ajaxStart and .ajaxStop seem to be malfunctioning

Having trouble displaying and hiding a loading gif before and after an Ajax request using ajaxStart and ajaxStop. Here's what I have: HTML: <div id="loading" style="display: none"><img src="loading.gif></div> JS: $("#searchForm") ...

What causes the variance in behavior between the Angular-formly directive and type?

I am facing an issue with two input fields that are both generated using the same template. I have set the required attribute to true for both of them by using the following code snippet: ... templateOptions: { ... required: true } One input fiel ...

Is there a way to make PrismaClient return DateTime fields as Unix timestamps rather than JavaScript date objects?

When utilizing the PrismaClient for database interaction, DateTime fields are returned as JavaScript Date objects instead of Unix timestamp numbers. Despite being stored as Unix timestamp numbers in the database itself, I require the dates to be retrieved ...

Unable to retrieve the state in a functional component using the useLocation hook from react-router-dom

I am facing an issue while trying to access a state property that I have passed through NavLink into a component. Despite using useLocation as recommended, I seem to be struggling with it due to some typescript error. Here is the code snippet: import Reac ...

In Angular, the process of duplicating an array by value within a foreach function is not

I have been attempting to duplicate an array within another array and make modifications as needed. this.question?.labels.forEach((element) => { element["options"] = [...this.question?.options]; // I've tried json.stringify() as wel ...

Slick Slider fails to load on web browsers

Hi everyone, I have a snippet of HTML code that I need help with: <!DOCTYPE html> <html> <head> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/jquery.slick/1.6.0/slick.css"/> </head> <body> ...

Upload the image data into the input file tag

Is there a way to take a string code stored in a JavaScript variable that is retrieved from the Cropit plugin (used for cropping images) and then set this information into an input file tag? I want to be able to send it via AJAX to PHP. Here is the JavaSc ...

JavaScript keydown event for rotating images

I am experiencing an issue with JavaScript animation. I have incorporated code from this particular link into my keydown function. However, the code is not functioning as expected. While the code from the provided link works fine on its own, within the key ...

Is there a method available to condense or merge PHP, DHTML, and JavaScript components efficiently?

The title may be a bit confusing, so let me explain my idea here: Imagine we have a component for a web page, such as a database-driven table or grid. This hypothetical "component" functions independently, relying on several mixed language files: HTML for ...

What is the best way to compare two arrays of ids in MongoDB to find matching elements?

I have 2 arrays with different ids : bikesWithNoOrders [id , id1 , id2] filteredResult [id3 , id5] Is there a way to query and find all of them at once? This is what I currently have: queryBuilder.find({ _id: { $in: bikesWithNoOrders } }); queryBuilde ...

Implementing autocompletion in an ASP.NET MVC4 application with AJAX and jQuery

I have been attempting to implement an autocomplete feature in a textbox that fetches company names. Despite successfully retrieving records and receiving them in the success function via Ajax, I am not seeing any suggested autocompletions in the textbox. ...

Invalid data format in the JSON response

The JSON response I am receiving from an AJAX $.getJSON() request seems to be malformed, and I'm struggling to identify the issue. Below is the code for the request: var myfunc = function(){ $.getJSON( "/", {"data": ""}, function( data, status ) ...