Testing the creation of elements dynamically with jestLooking into jest for dynamically adding

Attempting to test a dynamic element using TypeScript, but struggling to understand the process. Can anyone offer guidance?

Below is the TypeScript file:

export default class MyClass {
    constructor(){
        this.render();
    }

    render() {

        const el:HTMLInputElement = document.createElement('input') as HTMLInputElement;
        const link:HTMLAnchorElement = document.createElement('a') as HTMLAnchorElement;
        const container:HTMLBodyElement = document.querySelector('body') as HTMLBodyElement;

        link.innerHTML = "Click Me!";
        link.setAttribute('href', '#');
        link.setAttribute('target', '_blank');

        el.setAttribute('type', 'file');
        container.appendChild(el);
        container.appendChild(link);

        el.addEventListener('change', (event) => {
            if('files' in el) {
                const availFile = el.files[0];
                const blob = new Blob([availFile], { type: availFile.type});

                const objectURL = window.URL.createObjectURL(blob);
                link.setAttribute('href', objectURL);

            }
        })
    }
}


new MyClass();

The corresponding test.spec file:

import MyClass from "./index";
jest.mock('./index');

describe("initial testing", () => {

    it("constructor can be tested", () => {
        const myClass = new MyClass();
        expect(MyClass).toHaveBeenCalledTimes(1);
    });

    it('element rendering successful', () => {
        const myClass = new MyClass();
        myClass.render();
        expect(document.querySelector('input')).toBeTruthy();
        expect(document.querySelector('a')).toBeTruthy();
    })

});

Coverage report:

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |       75 |        0 |       75 |       75 |                   |
 index.ts |       75 |        0 |       75 |       75 |    21,22,23,25,26 |
----------|----------|----------|----------|----------|-------------------|

Seeking advice on how to test Uncovered Lines. Any assistance would be appreciated.

Answer №1

Below is the provided solution for unit testing:

index.ts:

export default class MyClass {
  constructor() {
    this.render();
  }

  render() {
    const el: HTMLInputElement = document.createElement('input') as HTMLInputElement;
    const link: HTMLAnchorElement = document.createElement('a') as HTMLAnchorElement;
    const container: HTMLBodyElement = document.querySelector('body') as HTMLBodyElement;

    link.innerHTML = 'Click Me!';
    link.setAttribute('href', '#');
    link.setAttribute('target', '_blank');

    el.setAttribute('type', 'file');
    container.appendChild(el);
    container.appendChild(link);

    el.addEventListener('change', (event) => {
      if ('files' in el) {
        const availFile: File = el.files![0];
        const blob = new Blob([availFile], { type: availFile.type });

        const objectURL = window.URL.createObjectURL(blob);
        link.setAttribute('href', objectURL);
      }
    });
  }
}

index.test.ts:

import MyClass from './index';

describe('testing as first', () => {
  let createElement;
  let querySelector;
  let createObjectURL;
  beforeEach(() => {
    createElement = document.createElement;
    querySelector = document.querySelector;
    createObjectURL = window.URL.createObjectURL;
  });
  afterEach(() => {
    jest.restoreAllMocks();
    document.createElement = createElement;
    document.querySelector = querySelector;
    window.URL.createObjectURL = createObjectURL;
  });
  it('should call render method', () => {
    jest.spyOn(MyClass.prototype, 'render').mockReturnValueOnce();
    const myClass = new MyClass();
    expect(myClass.render).toBeCalledTimes(1);
  });
  it('should render element and handle change event if input has files', () => {
    const myClass = new MyClass();
    const mAvailFile = new Blob(['I am file'], { type: 'text/html' });
    const mInput = { setAttribute: jest.fn(), addEventListener: jest.fn(), files: [mAvailFile] };
    const mLink = { setAttribute: jest.fn(), innerHTML: '' };
    const mContainer = { appendChild: jest.fn() };

    document.createElement = jest.fn().mockImplementation((tagName) => {
      switch (tagName) {
        case 'input':
          return mInput;
        case 'a':
          return mLink;
      }
    });
    document.querySelector = jest.fn().mockReturnValueOnce(mContainer);
    mInput.addEventListener.mockImplementationOnce((event, callback) => {
      callback();
    });
    const mObjectURL = 'blob:https://www.google.com/6e165b50-979b-43f6-b685-7163413f0faf';
    window.URL.createObjectURL = jest.fn().mockReturnValueOnce(mObjectURL);
    myClass.render();
    expect(document.createElement).toBeCalledTimes(2);
    expect(document.querySelector).toBeCalledWith('body');
    expect(mLink.innerHTML).toBe('Click Me!');
    expect(mLink.setAttribute.mock.calls[0]).toEqual(['href', '#']);
    expect(mLink.setAttribute.mock.calls[1]).toEqual(['target', '_blank']);
    expect(mInput.setAttribute).toBeCalledWith('type', 'file');
    expect(mContainer.appendChild.mock.calls[0]).toEqual([mInput]);
    expect(mContainer.appendChild.mock.calls[1]).toEqual([mLink]);
    expect(mInput.addEventListener).toBeCalledWith('change', expect.any(Function));
    expect(window.URL.createObjectURL).toBeCalledWith(new Blob([mAvailFile], { type: mAvailFile.type }));
    expect(mLink.setAttribute.mock.calls[2]).toEqual(['href', mObjectURL]);
  });

  it('should render element', () => {
    const myClass = new MyClass();
    const mInput = { setAttribute: jest.fn(), addEventListener: jest.fn() };
    const mLink = { setAttribute: jest.fn(), innerHTML: '' };
    const mContainer = { appendChild: jest.fn() };

    document.createElement = jest.fn().mockImplementation((tagName) => {
      switch (tagName) {
        case 'input':
          return mInput;
        case 'a':
          return mLink;
      }
    });
    document.querySelector = jest.fn().mockReturnValueOnce(mContainer);
    mInput.addEventListener.mockImplementationOnce((event, callback) => {
      callback();
    });
    myClass.render();
    expect(document.createElement).toBeCalledTimes(2);
    expect(document.querySelector).toBeCalledWith('body');
    expect(mLink.innerHTML).toBe('Click Me!');
    expect(mLink.setAttribute.mock.calls[0]).toEqual(['href', '#']);
    expect(mLink.setAttribute.mock.calls[1]).toEqual(['target', '_blank']);
    expect(mInput.setAttribute).toBeCalledWith('type', 'file');
    expect(mContainer.appendChild.mock.calls[0]).toEqual([mInput]);
    expect(mContainer.appendChild.mock.calls[1]).toEqual([mLink]);
    expect(mInput.addEventListener).toBeCalledWith('change', expect.any(Function));
  });
});

Unit tests completed with full coverage at 100%:

 PASS  src/stackoverflow/59833555/index.test.ts (14.002s)
  testing as first
    ✓ should call render method (6ms)
    ✓ should render element and handle change event if input has files (19ms)
    ✓ should render element (13ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 index.ts |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        16.304s, estimated 17s

Original source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59833555

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

Navigating the syntax of React with Typescript - Feeling lost

As I embark on my journey with Typescript/React, I find myself trying to decode the meanings behind it all. Coming from a C# background, this new environment presents a unique challenge for me. Despite going through several tutorials, I still find myself p ...

Angular 2 ngSubmit triggers unexpectedly on occasions when it is not supposed to

Currently, I am working on developing an Ionic 3 application with Angular 2 and TypeScript. In the app, there is a form that is responsible for sending data to our server. The issue I am facing is that whenever I click on the following button: <butto ...

Defining TypeScript class events by extending EventEmitter

I have a class that extends EventEmitter and is capable of emitting the event hello. How can I properly declare the on method with a specific event name and listener signature? class MyClass extends events.EventEmitter { emitHello(name: string): void ...

The element type 'x' in JSX does not offer any construct or call signatures

I have recently imported an image and I am trying to use it within a function. The imported image is as follows: import Edit from 'src/assets/setting/advertising/edit.png'; This is the function in question: function getOptions(row) { ...

typescript what type of functionality do dynamic class methods provide

I'm currently working on building a class that creates dynamic methods during the constructor stage. While everything is functioning properly, I've encountered an issue with VS Code's auto suggestion not recognizing these dynamic methods. Ho ...

"Converting to Typescript resulted in the absence of a default export in the module

I converted the JavaScript code to TypeScript and encountered an issue: The module has no default export I tried importing using curly braces and exporting with module.exports, but neither solution worked. contactController.ts const contacts: String[ ...

Circular structure error occurred when attempting to convert an object to JSON, starting at an object constructed with the constructor 'Object'

I am facing an issue where I need to update a Medico from the collection, and I have successfully destructured the data of the Medico's name and email. Additionally, I have obtained the ID of the assigned hospital. However, I am having trouble sendin ...

What is the correct way to invoke a function that accepts a combination of specific string values when all I have is a regular string?

Within the TypeScript function declaration provided below, the parameter type alignment consists of unioned literals. function printText(s: string, alignment: "left" | "right" | "center") { // ... } As per the documentation ...

Change the name of the interface from the imported type

When working with Google Apps Script, I have implemented the Advanced Calendar Service, originally labeled as "Calendar". However, I have renamed it to "CalendarService". How can I incorporate this name change when utilizing the type definitions for Apps S ...

The component "react-dnd-html5-backend" does not have a defined export called 'HTML5Backend'

What is the solution to resolve this issue? The error message states that 'react-dnd-html5-backend' does not have an exported member named 'HTML5Backend'. https://i.sstatic.net/QUZut.jpg ...

Steps to create a formGroup in Angular 12

import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-signup', templateUrl: './signup.component.html', ...

When a selection is made in React MUI Virtualized Autocomplete, the autocomplete menu scrolls back to the top

I am currently using reactMUI autocomplete with virtualization because the listbox is expected to contain thousands of items. The virtualization feature works fine, but when I add an onchange function, the listbox automatically scrolls back to the top wh ...

I have a quick question: What is the most effective method for creating PDF templates with Angular and .NET 6, specifically for designs that feature heavy

Seeking the optimal solution for creating PDF templates using Angular and .NET 6? Specifically looking to design templates that heavily feature tables. In my exploration of efficient PDF template creation with Angular and .NET 6, I ventured into using pdf ...

When attempting to transfer data from the parent component to child components, the data is appearing as undefined in the display

I have been working on passing data from a parent component to child components, but I keep encountering an issue where the data is showing as undefined. Below is the code snippet: Parent Component In this section, I have declared the variable part_data ...

Troubleshooting: Why are my Angular 8 Carousel Slide Animations not functioning

Looking to create a carousel slideshow with images sliding from right to left and smoothly transition to the next image. All the necessary code can be found in this STACKBLITZ Here is the HTML snippet: <ngb-carousel *ngIf="images" [showNavigationArro ...

Using node-fetch version 3.0.0 with jest results in a SyntaxError stating that import statements cannot be used outside a module

Recently, I've been updating my API to utilize node-fetch 3.0.0. One major change highlighted in their documentation is that node-fetch is now a pure ESM module. Click here for more information on the changes This update caused some of my unit tests ...

Issue with Angular Compilation When Importing Library Function into Web Worker

I am facing an issue with a web worker in Angular that used to function properly in the previous versions: /// <reference lib="webworker" /> import { ParseResult } from "papaparse"; import { readCSV } from '@fireflysemantics/ ...

A TypeScript function that returns the ReturnType of a specific callback function

Is it possible to define an annotation for a function that accepts a callback, and have the function return type determined by the callback's return type? // Suppose the callback takes a number as argument function processCallback(cb: (arg:number) =&g ...

Looking for a button that can be toggled on and off depending on the input fields

Even after adding useEffect to my code, the button component remains disabled unless the input fields are filled. It never enables even after that. export default function Page() { const [newPassword, setNewPassword] = useState(''); const [conf ...

What is the best way to handle closing popups that have opened during an error redirection

In my interceptor, I have a mechanism for redirecting the page in case of an error. The issue arises when there are popups already open, as they will not automatically close and the error page ends up appearing behind them. Here's the code snippet re ...