Is there a way to properly test a Vue component that is watching a ref prop?

I am currently testing a Vue component that should display a success icon for 3 seconds when the loading state changes from true to false. I have encountered challenges while trying to write a component test using Vue Testing Library or Vue test utils due to various reasons (such as issues with props in mount/render and differences in watcher behavior). I am contemplating changing my approach by testing a composable instead of directly testing the component, although I anticipate facing similar difficulties. Any suggestions or ideas on how to tackle this?

<script setup lang="ts">
interface Props {
  loading?: boolean;
  label?: string;
  error?: boolean;
  loadingSuccessText?: string;
  loadingSuccessIcon?: string;
}

const props = withDefaults(defineProps<Props>(), {
  loading: false,
  label: "button",
  error: false,
  loadingSuccessText: "Success",
  loadingSuccessIcon: "circle-check",
});

const loadingTimeoutId = ref<number | undefined>(undefined);
const clicking = ref(false);
const loading = toRef(props, "loading");

const loadingFinished = computed(() => !!loadingTimeoutId.value);

watch(loading, (newVal, oldVal) => {
  if (newVal != oldVal && !newVal) return;
  clearTimeout(loadingTimeoutId.value);
  loadingTimeoutId.value = setTimeout(() => {
    loadingTimeoutId.value = undefined;
  }, 3000);
});
</script>

<template>
  <button
    id="button"
    :aria-label="label"
    :class="['gradient-button', clicking ? 'gradient-button--clicked' : '']"
    @mousedown="clicking = true"
    @mouseup="clicking = false"
    @mouseleave="clicking = false"
  >
    <span v-if="loadingFinished && !loading && !error">
      {{ loadingSuccessText }}
      <FontAwesomeIcon :icon="loadingSuccessIcon" data-testid="success" />
    </span>
    <slot v-else-if="!loading"></slot>
    <span v-else class="loader loader--small" data-testid="spinner"></span>
  </button>
</template>

Answer №1

If you want to create the testing code, consider the following approach:

import { mount } from '@vue/test-utils';
import YourComponent from '@/path/to/YourComponent.vue';

describe('YourComponent', () => {
  it('checks prop changes', async () => {
    const wrapper = mount(YourComponent, {
      props: {
          loading: true,
      },
    });

    await wrapper.setProps({ loading: false });
    expect(wrapper.vm.loadingTimeoutId).toBeDefined();
    await new Promise((resolve) => setTimeout(resolve, 3000));
    expect(wrapper.vm.loadingTimeoutId).toBeUndefined();
  });
});

Alternatively, you can wrap the code in a function for easier testing and better composability:

// loading.ts
export default (props: any) => {
  const loading = toRef(props, "loading");
  const loadingTimeoutId = ref<number | undefined>(undefined);
  watch(loading, (newVal, oldVal) => {
    if (newVal != oldVal && !newVal) return;
    clearTimeout(loadingTimeoutId.value);
    loadingTimeoutId.value = setTimeout(() => {
      loadingTimeoutId.value = undefined;
    }, 3000);
  });
  return {
    loading,
    loadingTimeoutId
  }
}

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

Is there a way to confirm whether or not two files are identical?

Is there a reliable method to determine if two files are identical? I've been using a solution that involves downloading the first part of each file, converting the data to base64, and then comparing them. However, I've encountered an issue wher ...

Getting an error message of 'Unable to locate Firebase Storage Default Bucket on the server?

I'm currently facing an issue with the server not being able to locate the bucket. To troubleshoot, I've stored the token and other crucial details in a separate file as a string. Afterwards, I split it and utilize the relevant text in my Javascr ...

Determine the shared elements between two distinct arrays and create an object that includes these common elements

I am dealing with 2 different arrays of objects var array1 = [ {id: 1, name:'fruit', rating:5}, {id: 4, name:'vegetable', rating: 3}, {id: 8, name:'meat', rating:1} ]; var array2 = [ {alimentId: 1, quantity: 2}, {alimentId: 4 ...

Converting variable data into a JSON array format

Currently, I am in the process of setting up header bidding with Prebid. My approach involves loading the desired ads from a database using PHP and MySQL, followed by creating text variables in JavaScript with the PHP data. The issue arises when I attempt ...

React is unable to identify the `InputProps` prop when applied to a DOM element

https://i.sstatic.net/7fZmn.png Caution: React is not able to identify the InputProps property on a DOM element. If you intend for it to be displayed in the DOM as a custom attribute, spell it in lowercase as inputprops. If you accidentally passed it from ...

How can I loop through JSON in AngularJS to populate fields without knowing the key value?

Here is the data structure that I'm working with: { "0": { "keyword": "flower", "short_desc": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "pt_value": "5" }, "1": { "keyword": "tree", "short_desc": "Lorem ipsum dolor sit amet, consecte ...

The feature user.presence.status is now operational in discord.js v13

I was trying to get my bot to rename a channel to show the number of users currently online. I wanted to specifically filter out users who are online, idle, or in do-not-disturb mode, as well as offline users. However, the code I wrote doesn't seem to ...

Implementing objects in a three.js scene without displaying them on the screen

I have a function called createCylinder(n, len, rad) that is being invoked from another function named createScene(). Despite checking that the vertices and faces are correctly added without errors, the geometry itself is not rendering. I suspect this is ...

Bootstrap relies on jQuery for its JavaScript functionality, so jQuery must be loaded before using Bootstrap's JavaScript

I encountered an issue while trying to load the Bootstrap library, consistently receiving this error message: Uncaught Error: Bootstrap's JavaScript requires jQuery Even though I have ensured that jQuery is loaded before attaching the Bootstrap li ...

Tips for implementing an autoscroll feature in the comments section when there is an abundance of comments

Having a large number of comments on a single post can make my page heavy and long sometimes. This is the current layout of my post comment system: Post 1 Comment for post 1 //if comments are more than 3 <button class="view_comments" data-id="1">Vi ...

Switch between two PHP files with a toggle feature using jQuery click event

Need help with toggling between two PHP files - cab.php and d3.php. Toggling within the same file works fine, but encountering issues when trying to toggle from one file to another. function botaod2mostraesconde() { $(".wrapd2").toggle(); $( ...

Identifying Javascript-Set Cookies on the Client-Side: A Comprehensive Guide

Currently, I am using Python and Selenium for my project. I'm trying to determine if a specific cookie is set through JavaScript. Is there a method or approach that can help me accomplish this? ...

The Ajax call is successful, however, there is no update made to the database

I am currently working on a JavaScript script to allow for profile editing without having to reload the page, similar to how it was done on Facebook. However, I am facing an issue where the AJAX function returns success but does not make any changes in the ...

Switching from using ngRouter to ui-router is a common

After purchasing an angularjs template for my application, I noticed that ngRouter was used instead of ui-router, which I am more comfortable with. So, I decided to switch to ui-router and update all the routes. However, I encountered some extra code in my ...

Creating a dropdown menu in Bootstrap 4 using JSON information

I am trying to create a dynamic drop-down menu using an input field with a drop-down button inside a form. Currently, I am attempting to populate the drop-down menu with static JSON data. However, I am encountering issues with getting it to function proper ...

Managing file responses in Node.js

Hey there! I've been working on implementing ajax image upload and here's how far I've gotten. This is my index.html: <!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8> <title>File Upload ...

Invalid web address entered

While attempting to incorporate a functionality that opens a new window to a specific link when clicking an icon, I encountered an issue where the click action redirects to the wrong URL. Instead of directing to the URL specified in its href attribute, it ...

ReactKonva does not have compatibility with the element type "div"

I am trying to create a circle with ReactKonva and display a tooltip when hovering over it. In React, we are required to return a single element from the render method. To achieve this, I attempted to wrap both the tooltip and the ReactKonva element within ...

Tips for preloading a TypeScript class using the -r option of ts-node

Imagine you have a file called lib.ts that contains the following code: export class A {} console.log('script loaded'); Now, if you launch the ts-node REPL using the command: npx ts-node -r ./lib.ts, you will see that it outputs "script loaded," ...

Steps to resolve the issue: The current experimental syntax 'classProperties' is not supported

I encountered an issue where the experimental syntax 'classProperties' is not supported when trying to run my react js application. My goal is to increment the value inside a <span> when a ` button is clicked. Below is the excerpt of my c ...