Unable to view the refreshed DOM within the specifications after it has been altered

For my current project, I am working on writing a functional spec that involves using Mocha/JSDOM and making assertions with 'chai'.

The specific use case I am tackling is related to the function called updateContent:

  1. When this function is executed, it internally triggers another function to fetch HTML content.
  2. After retrieving the HTML content, it needs to be processed and added to the existing DOM elements.

While the functionality works smoothly when run on the server, I encounter an issue when attempting to write a test specification. The problem arises where the updated DOM is not visible during the testing phase. Upon inspecting the updateContent function by inserting console statements, I indeed witness the updated content. However, once control is passed to the spec function, only the original DOM added to JSDOM is displayed.

The implementation involves TypeScript, JavaScript combination, and the utilization of jQuery for DOM operations.

I would greatly appreciate it if you could offer any insights or suggestions on what might be missing in this scenario. I have already tried utilizing globals while accessing the necessary components.

The updateContent function can be found in the helper.js file

function updateContent(year, fetchAge) {
    Promise.all([fetchAge("age")]).then((data) => {
        console.log("DOM before update ="+$('html').html());
        data = data[0].replace(/{{age}}/g, year);
        $('.mybenifits .content').html(data);
        console.log("DOM after update ="+$('html').html());//Able to see the updated DOM
        console.log("$('.mybenifits .content').html="+global.$('.mybenifits .content').html());
    }).catch((error) => {
        console.log(" ******* Error while fetching age info");
    });
}

The code snippet for the Spec: helper.test.js


const expect = require('chai').expect;
const assert = require('chai').assert;
const sinon = require('sinon');
const { JSDOM } = require('jsdom');


const { updateContent } = require('../../main/webpack/common/helper.js');

describe('Helper - Example',  () => {
    it('should update the content', () => {  
        let htmlStr = '<!doctype html><html><body><div class="mybenifits"><div class="content"></div></div></body></html>';
        const jsdom = new JSDOM(htmlStr, {
            url: 'http://localhost/',
        });
        //Setting Global variables - start
        global.window = jsdom.window;
        global.document = jsdom.window.document;
        global.$ = require('jquery');
        //Setting GLobal variables - end
        
        //Mocking fetchAge function
        function fetchAge(featurename) {
            return '<p id="fcontent">Current Age is {{age}}</p>';
        }
        updateContent("2020",fetchAge);
        console.log("Total html file ="+$('html').html());
        //expect($('.mybenifits .content').html()).to.be.equal('<p id="fcontent">Current Age is 2020</p>');
        //expect(global.$('.mybenifits .content').html()).to.be.equal('<p id="fcontent">Current Age is 2020</p>');//Not working even if I use global 
    });
});

    

Answer №1

Your issue may be arising because the updateContent function is performing asynchronous operations while the rest of the code continues to execute. This could mean that the expect assertions are being triggered before the updateContent function completes its updates on the spec.

It's highly recommended to gain a solid understanding of Promises, how they work, and the usage of async/await keywords as it will be beneficial (and possibly necessary) in the long term.

The following snippet is UNTESTED, but it should guide you in the right direction. It assumes that your fetchAge() function returns a Promise, possibly fetching age data from an API or comparable source.

// Since only one promise is being resolved, `Promise.all` is unnecessary. You can chain `then` directly onto the returned promise from `fetchAge`.
// Additionally, the promise is being explicitly `return`ed for potential future use with `updateContent().then(...)`.
function updateContent(year, fetchAge) {
    return fetchAge("age")
        .then((data) => {
            // ...
            data = data[0].replace(/{{age}}/g, year);
            $('.mybenifits .content').html(data);
            // ...
        })
        .catch(/* ... */);
}

Implementing these changes should allow you to conduct tests like this. Remember when testing asynchronous code using `then` chains, utilizing the `done` parameter is crucial to notify jest when the test has completed. Refer to https://jestjs.io/docs/en/asynchronous for more information.

it('should update the content', (done) => {  
    // ...

    // Ensuring that the mock function which returns a promise behaves accordingly
    function fetchAge(featurename) {
        return Promise.resolve('<p id="fcontent">Current Age is {{age}}</p>');
    }

    updateContent("2020", fetchAge)
      .then(() => {
          console.log("Total html file="+$('html').html());
          expect($('.mybenifits .content').html()).to.be.equal('<p id="fcontent">Current Age is 2020</p>');
          expect(global.$('.mybenifits .content').html()).to.be.equal('<p id="fcontent">Current Age is 2020</p>');
          done();
      })
      .catch(error => done(e));
});

EDIT: For your reference, here is a potential rewrite of the updateContent function using async/await with inline types. Please note that this is untested locally and may require adjustments :)

// Introducing the `async` keyword
async function updateContent(year: string, fetchAge: () => Promise<string>): Promise<void> {
    // Implementing async/await allows the use of `try/catch` blocks instead of `<Promise>.catch()`!
    try {
        // Utilizing `await` since `fetchAge` returns a Promise
        const data = await fetchAge("age");
        const htmlData = data[0].replace(/{{age}}/g, year);
        $('.mybenifits .content').html(htmlData);
    } catch (error) {
        // Add error handling/logging here
        console.error(error);
    }
}

In your test, simply wait for the completion of `updateContent` before asserting DOM values!

it('should update the content', async () => {
    // ...
    await updateContent("2020", fetchAge);
    expect($('.mybenifits .content').html()).to.be.equal('<p id="fcontent">Current Age is 2020</p>');
    expect(global.$('.mybenifits .content').html()).to.be.equal('<p id="fcontent">Current Age is 2020</p>');
    // ...
});

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

Using destructuring assignment in a while loop is not functional

[a,b] = [b, a+b] is ineffective here as a and b are always set to 0 and 1. However, using a temporary variable to swap the values does work. function fibonacciSequence() { let [a, b, arr] = [0, 1, []] while (a <= 255) { arr.concat(a) [a, ...

Error when sending Angular 4 GET request with multiple Headers results in a 400 bad request code

I've been attempting to perform a POST request with headers in order to receive a response. The Angular code snippet I'm currently using for this request is shown below: const headers = new HttpHeaders({ 'Content-Type': 't ...

TypeScript encountered an unexpected { token, causing a SyntaxError

Despite multiple attempts, I can't seem to successfully run my program using the command "node main.js" as it keeps showing the error message "SyntaxError: Unexpected token {" D:\Visual Studio Code Projects\ts-hello>node main.js D:&bsol ...

Inspect each page to confirm that the user has verified that they are of legal age

I'm currently developing a website (tobacco-related) that imposes an age validation requirement on all visitors before granting access to the site. I've implemented a form for age verification, but I'm stuck at how to store a cookie upon pas ...

Can TypeScript interfaces be used with various types?

Currently, I am receiving data from an endpoint that is providing a collection of different types all following the same interface. The structure looks something like this: interface CommonInterface { public type: string; public commonProperty1: i ...

Adjust Fabric js Canvas Size to Fill Entire Screen

Currently, I am working with version 4.3.1 of the Fabric js library and my goal is to adjust the canvas area to fit its parent div #contCanvasLogo. I have tried multiple approaches without success as the canvas continues to resize on its own. Below is the ...

Utilizing TypeScript 3.1: Easier Array Indexing with Enums in Strict Mode

Enabling TypeScript "strict" mode with "noImplicitAny" causes this code to fail compilation. I am looking for guidance on how to properly declare and use Arrays indexed by Enum values. namespace CommandLineParser { enum States { sNoWhere, sSwitchValu ...

Troubleshooting the issue of having multiple menu items in Material UI

Every time I attempt to add the Menu component multiple times, I encounter an issue with the popup list displaying incorrectly. To demonstrate this problem, you can view it through the link provided on codesandbox below. I have included data-id attributes ...

Having trouble pushing data to a GraphQL database from my Next.js application

I am currently working on developing a Reddit-like platform. To simplify the process, I have integrated SQL with graphQL using Stepzen for endpoints. Below is the code snippet of my Post Box component for the site, which includes graphQL mutations.ts and q ...

When using v-for to render an array list fetched from AsyncData, an error is thrown: The virtual DOM tree rendered on the client-side does not match the one

For my application, I am utilizing Nuxt.js. On one of the pages, I am using AsyncData to fetch an array of data objects from my API asynchronously. These data objects are then rendered in my template using v-for. Everything is functioning properly until I ...

Tips for preventing the use of nested functions while working with AJAX?

Consecutively making asynchronous calls can be messy. Is there a cleaner alternative? The issue with the current approach is its lack of clarity: ajaxOne(function() { // do something ajaxTwo(function() { // do something ajaxThree() }); }); ...

The Express server's `GET` request at the root does not seem

When I access localhost:8080/chat?k=1&d=1, the console displays "CHAT PAGE" and everything works correctly. However, when I try to visit localhost:8080, the root event does not display "INDEX PAGE" as expected. Instead, it automatically retrieves ind ...

Error: Undefined object while trying to access 'injection' property

After updating to React 16.x, I encountered the following error: Uncaught TypeError: Cannot read property 'injection' of undefined at injectTapEventPlugin (injectTapEventPlugin.js:23) at eval (index.js:53) at Object.<anonymous> ...

Instructions on incorporating domains into next.config.js for the "next/image" function with the help of a plugin

Here is the configuration I am currently using. // next.config.js const withImages = require("next-images"); module.exports = withImages({ webpack(config, options) { return config; }, }); I need to include this code in order to allow images from ...

Setting a fixed data value within a div for subsequent retrieval through a function

I found a helpful example that demonstrates how to convert numbers into words. You can check it out here. The function for converting numbers into words is implemented in the following HTML code: <input type="text" name="number" placeholder="Number OR ...

Error in code - message gets sent immediately instead of waiting for timeout to occur

There seems to be an issue with the code where the message is sent instantly instead of waiting for the specified timeout duration. Based on the code, it should wait for the time mentioned in the message. I'm puzzled as to why it's not functioni ...

Unable to modify module variable through monkey patching in Python unit tests

I am currently testing the module: package/module.py DATA_PATH = os.path.join(os.path.dirname(__file__), "data") class SomeClass: def __init__(self): self.filename = os.path.join(DATA_PATH, "ABC.txt") within my tests in module_test.py I have ...

Surprising outcome arising from simultaneous execution of numerous asynchronous operations on every individual object within an array

I'm fairly new to working with Node.js and I'm still trying to grasp the concept of callbacks and the asynchronous nature of Node.js. However, I've encountered a problem that I can't seem to solve. I've already searched extensively ...

Guidelines for displaying user profile information in the dashboard page through an Angular project, considering the different user types (user, super user, admin)

In my application, I have implemented the dashboard feature. There are three types of dashboards: the regular user dashboard, super user dashboard, and admin dashboard. The super user and admin dashboards include additional tables along with the data from ...

What is the function of '@' symbol in coding?... obtain {ModuleName} from '@ModuleName'

What is the significance of the '@' symbol in imports? I've noticed that various modules like '@react-navigation', '@babel', and others use the '@' symbol. Does this symbol serve a specific purpose, or is it s ...