GithubHelp home page GithubHelp logo

trurl-master / jsdom-testing-mocks Goto Github PK

View Code? Open in Web Editor NEW
106.0 3.0 5.0 1.57 MB

A set of tools for emulating browser behavior in jsdom environment

License: MIT License

TypeScript 99.71% JavaScript 0.29%
testing react-testing-library jsdom mocks intersectionobserver resizeobserver viewport matchmedia javascript reactjs

jsdom-testing-mocks's Introduction

jsdom-testing-mocks

A set of tools for emulating browser behavior in jsdom environment

Build status version PRs Welcome downloads MIT License Code of Conduct

GitHub Repo stars Twitter URL

Stand With Ukraine

Mocks

matchMedia
Intersection Observer
Resize Observer
Web Animations API

Installation

npm i -D jsdom-testing-mocks
yarn add -D jsdom-testing-mocks

Setup

With react

To avoid having to wrap everything in act calls, you can pass act to configMocks:

import { configMocks } from 'jsdom-testing-mocks';
import { act } from '...';

configMocks({ act });

It can be done in a setup file, or in a test file, before rendering the component.

With vitest

Some mocks require lifecycle hooks to be defined on the global object. To make it work with vitest, you need to enable globals in your config. If you don't want to do that you can pass it manually using configMocks.

Also, if you're using fake timers, at the time of writing this, vitest doesn't enable faking performance.now, requestAnimationFrame and cancelAnimationFrame by default, so you need to do it manually:

vi.useFakeTimers({
  toFake: [
    // vitests defaults
    'setTimeout',
    'clearTimeout',
    'setInterval',
    'clearInterval',
    'setImmediate',
    'clearImmediate',
    'Date',
    // required for mocks
    'performance',
    'requestAnimationFrame',
    'cancelAnimationFrame',
  ],
});

vitest defaults

Testing framework support

We aim to support all major testing frameworks that support jsdom. Internally, there are no dependencies on any of them, so it's likely that it will work out of the box. Currently tested and confirmed to work with jest, @swc/jest and vitest (with some setup). If you encounter any problems with other testing frameworks, please open an issue.

Mock viewport

Mocks matchMedia, allows testing of component's behavior depending on the viewport description (supports all of the Media Features). mockViewport must be called before rendering the component

Example, using React Testing Library:

import { mockViewport } from 'jsdom-testing-mocks';

it('shows the right lines on desktop and mobile', () => {
  const viewport = mockViewport({ width: '320px', height: '568px' });

  render(<TestComponent />);

  expect(
    screen.getByText('Content visible only on small screens')
  ).toBeInTheDocument();

  expect(
    screen.queryByText('Content visible only on large screens')
  ).not.toBeInTheDocument();

  act(() => {
    viewport.set({ width: '1440px', height: '900px' });
  });

  expect(
    screen.queryByText('Content visible only on small screens')
  ).not.toBeInTheDocument();

  expect(
    screen.getByText('Content visible only on large screens')
  ).toBeInTheDocument();

  viewport.cleanup();
});

Also, you can mock the viewport for a group of tests, using mockViewportForTestGroup:

import { mockViewportForTestGroup } from 'jsdom-testing-mocks'

describe('Desktop specific tests', () => {
  mockViewportForTestGroup({ width: '1440px', height: '900px' })

  test('this', () = {
    // ...
  })

  test('that', () = {
    // ...
  })
})

Mock IntersectionObserver

Provides a way of triggering intersection observer events

Example, using React Testing Library:

import { mockIntersectionObserver } from 'jsdom-testing-mocks';

const io = mockIntersectionObserver();

/*
Assuming html:
<div data-testid="container">
  <img src="..." alt="alt text" />
</div>

And an IntersectionObserver, observing the container
*/
it('loads the image when the component is in the viewport', () => {
  const { container } = render(<TestComponent />);

  expect(screen.queryByAltText('alt text')).not.toBeInTheDocument();

  // when the component's observed node is in the viewport - show the image
  act(() => {
    io.enterNode(screen.getByTestId('container'));
  });

  expect(screen.getByAltText('alt text')).toBeInTheDocument();
});

API

mockIntersectionObserver returns an object, that has several useful methods:

.enterNode(node, desc)

Triggers all IntersectionObservers observing the node, with isIntersected set to true and intersectionRatio set to 1. Other IntersectionObserverEntry params can be passed as desc argument, you can override any parameter except isIntersected

.leaveNode(node, desc)

Triggers all IntersectionObservers observing the node, with isIntersected set to false and intersectionRatio set to 0. Other IntersectionObserverEntry params can be passed as desc argument, you can override any parameter except isIntersected

.enterNodes(nodeDescriptions)

Triggers all IntersectionObservers observing the nodes in nodeDescriptions with multiple nodes entering at once. Each IntersectionObserver callback will receive only the nodes it's observing:

io.enterNodes([
  // you can pass multiple nodes each with its own state
  { node: screen.getByText('First Node'), desc: { intersectionRatio: 0.5 } },
  // description is optional:
  { node: screen.getByText('Second Node') },
  // or you can use a shorthand:
  screen.getByText('Third Node'),
]);

.leaveNodes(nodeDescriptions)

Triggers all IntersectionObservers observing the nodes in nodeDescriptions with multiple nodes leaving at once. Each IntersectionObserver callback will receive only the nodes it's observing.

.triggerNodes(nodeDescriptions)

Triggers all IntersectionObservers observing the nodes in nodeDescriptions with multiple nodes at once with custom descriptions (isIntersected is not enforced). Each IntersectionObserver callback will receive only the nodes it's observing

.enterAll(desc) and .leaveAll(desc)

Triggers all IntersectionObservers for each of the observed nodes

Mock ResizeObserver

Mocks ResizeObserver class. Resize callbacks are triggered manually using resize method returned by the mock. Elements' size must not be 0 (at least on one axis) for the element to appear in the list of callback entries (you can mock the size using mockElementSize or mockElementBoundingClientRect)

Example, using React Testing Library:

import { mockResizeObserver } from 'jsdom-testing-mocks';

const DivWithSize = () => {
  const [size, setSize] = useState({ width: 0, height: 0 });
  const ref = useRef(null);

  useEffect(() => {
    const observer = new ResizeObserver((entries) => {
      setSize({
        width: entries[0].contentRect.width,
        height: entries[0].contentRect.height,
      });
    });

    observer.observe(ref.current);

    return () => {
      observer.disconnect();
    };
  }, []);

  return (
    <div data-testid="theDiv" ref={ref}>
      {size.width} x {size.height}
    </div>
  );
};

const resizeObserver = mockResizeObserver();

it('prints the size of the div', () => {
  render(<DivWithSize />);

  const theDiv = screen.getByTestId('theDiv');

  expect(screen.getByText('0 x 0')).toBeInTheDocument();

  resizeObserver.mockElementSize(theDiv, {
    contentBoxSize: { inlineSize: 300, blockSize: 200 },
  });

  act(() => {
    // on the first run you don't have to pass the element,
    // it will be included in the list of entries automatically
    // because of the call to .observe
    resizeObserver.resize();
  });

  expect(screen.getByText('300 x 200')).toBeInTheDocument();

  resizeObserver.mockElementSize(theDiv, {
    contentBoxSize: { inlineSize: 200, blockSize: 500 },
  });

  act(() => {
    // on subsequent calls to `resize` you have to include it
    // explicitly, unless observe has been called on it again
    resizeObserver.resize(theDiv);
  });

  expect(screen.getByText('200 x 500')).toBeInTheDocument();
});

Caveats

Triggering the callback on observe

Although the mock doesn't call the resize callback on its own, it keeps track of all the cases when it should be implicitly called (like when the element first begins being observed), and it auto-adds them to the list of elements when resize is called. You can disable this in ResizeOptions

Mocking element's size

The mock uses the size provided by mockElementSize if present and fallbacks to getBoundingClientRect (that you can mock using mockElementBoundingClientRect). The issue with getBoundingClientRect however is that in the real world the value it returns takes CSS Transforms into account, while the values returned in the observer callback don't. It doesn't really matter because it is you who mocks sizes, but for consistency it is preferred that you use mockElementSize

API

mockResizeObserver returns an object, that has several methods:

.resize(elements?: HTMLElement | HTMLElement[], options: ResizeOptions)

Triggers all resize observer callbacks for all observers that observe the passed elements. Some elements are implicitly resized by the Resize Observer itself, for example when they first attached using observe. This mock doesn't call the callback by itself. Instead, it adds them to the list of entries when the next resize is called (it happens only once per observe per element).

In this example the resize callback will be triggered with all observed elements from within TestedComponent:

// a component that begins to observe elements in a useEffect
render(<TestedComponent />);

// ...don't forget to mock sizes

act(() => {
  // triggers the `resize` callback with the elements for which `observe` has been called
  resizeObserver.resize();
});
ResizeOptions.ignoreImplicit (false by default)

If true, do not include imlicit elements in the resize callback entries array

.mockElementSize(element: HTMLElement, size: Size)

Mocks element's size only for the ResizeObserver. size accepts 2 properties: contentBoxSize and borderBoxSize they're both similar to what you see in the ResizeObserver's callback entry. At least one of them must be present (if the other isn't it is set to be equal to the one present), and the other entry properties are derived from these two (and window.devicePixelRatio).

Example:

mockElementSize(myDiv, {
  // both contentBoxSize and borderBoxSize accept plain objects instead of arrays
  contentBoxSize: { inlineSize: 400, blockSize: 200 },
});

mockElementSize(myOtherDiv, {
  // only one dimension is required, the other one will be assumed to be 0
  borderBoxSize: { inlineSize: 200 },
});

.getObservers(element?: HTMLElement)

Returns all observers (observing the element if passed)

.getObservedElements(observer?: ResizeObserver)

Returns all observed elements (of the observer if passed)

Mock Web Animations API

Warning: experimental, bug reports, tests and feedback are greatly appreciated

Mocks WAAPI functionality using requestAnimationFrame. With one important limitation — there are no style interpolations. Each frame applies the closest keyframe from list of passed keyframes or a generated "initial keyframe" if only one keyframe is passed (initial keyframe removes/restores all the properties set by the one keyframe passed). As the implementation is based on the official spec it should support the majority of cases, but the test suite is far from complete, so here be dragons

Example, using React Testing Library:

import { mockAnimationsApi } from 'jsdom-testing-mocks';

const TestComponent = () => {
  const [isShown, setIsShown] = useState(false);

  return (
    <div>
      {/* AnimatePresence is a component that adds its children in the dom
          and fades it in using WAAPI, with 2 keyframes: [{ opacity: 0 }, { opacity: 1 }],
          also adding a div with the word "Done!" after the animation has finished
          You can find implementation in examples
       */}
      <AnimatePresence>{isShown && <div>Hehey!</div>}</AnimatePresence>
      <button
        onClick={() => {
          setIsShown(true);
        }}
      >
        Show
      </button>
    </div>
  );
};

mockAnimationsApi();

it('adds an element into the dom and fades it in', async () => {
  render(<TestComponent />);

  expect(screen.queryByText('Hehey!')).not.toBeInTheDocument();

  await userEvent.click(screen.getByText('Show'));

  // assume there's only one animation present in the document at this point
  // in practice it's better to get the running animation from the element itself
  const element = screen.getByText('Hehey!');
  const animation = document.getAnimations()[0];

  // our AnimatePresence implementation has 2 keyframes: opacity: 0 and opacity: 1
  // which allows us to test the visibility of the element, the first keyframe
  // is applied right after the animation is ready
  await animation.ready;

  expect(element).not.toBeVisible();

  // this test will pass right after 50% of the animation is complete
  // because this mock doesn't interpolate keyframes values,
  // but chooses the closest one at each frame
  await waitFor(() => {
    expect(element).toBeVisible();
  });

  // AnimatePresence will also add a div with the text 'Done!' after animation is complete
  await waitFor(() => {
    expect(screen.getByText('Done!')).toBeInTheDocument();
  });
});

Using with fake timers

It's perfectly usable with fake timers, except for the issue with promises. Also note that you would need to manually advance timers by the duration of the animation taking frame duration (which currently is set to 16ms in jest/sinon.js) into account. So if you, say, have an animation with a duration of 300ms, you will need to advance your timers by the value that is at least the closest multiple of the frame duration, which in this case is 304ms (19 frames * 16ms). Otherwise the last frame may not fire and the animation won't finish.

Current issues

  • Needs more tests

jsdom-testing-mocks's People

Contributors

adjsky avatar chromeq avatar davidr64 avatar trurl-master avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

jsdom-testing-mocks's Issues

ResizeObserver should fire the onResize handler when ro.observe(elem) is called

Hello,

Thanks for this lib! It's awesome!

The issue

I noticed one issue - per the whatwg spec:

Observation will fire when observation starts if Element is being rendered, and Element’s size is not 0,0.

Here's a quick example in the Chrome debugger:

  1. Create resizeobserver
  2. observe an element that's not in the DOM, no resize event fired
  3. observe an element in the DOM (has a size already), resize event fired immediately.

2022-06-27T16_10_480x257_screenshot

You can see this (I think... I don't speak C++) in Chromium here: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/resize_observer/resize_observer.cc;l=97?q=ResizeObserver&ss=chromium

Possible solution:

Can we extract the code from resize (here) that looks up sizes and call it as well from observe?

Further notes

I'm not sure if these are bugs as well, but:

  1. I don't think the onResize handlers should be called if the elements size is (0,0)
  2. I don't think the onResize handler should be called on an element if the element is not present in the DOM

Do not require window at import time

We run half of our tests in a node environment using the magic comments in Jest.

This causes the import of 'jsdom-testing-mocks' in setupTests.ts to fail.

Unfortunately, a workaround with a dynamic import is working locally but not in CI.

`@media not all and ` is not supported

Hi, thank you for your awesome library.)
I have been trying to test my own library and I encountered strange behavior.

Screenshot 2023-05-22 at 23 12 23

But, it's work in a browser.

I'm using jsdom-testing-mocks v1.9.0.

TypeError: Cannot redefine property: ResizeObserver (v1.4.0)

Hi

with version 1.4.0 this error starts to appear.

Node version 14.20.0
Jest version 27.5.1
jsdom => [email protected]

 FAIL  src/components/object-message/object-message.unit.js
  ● Test suite failed to run

    TypeError: Cannot redefine property: ResizeObserver
        at Function.defineProperty (<anonymous>)

       6 | import { click, getByText, mountToDom } from '@testutils'
       7 |
    >  8 | const resizeObserverMock = mockResizeObserver()
         |                            ^
       9 |
      10 | jest.mock('lodash/debounce', () => {
      11 |   return jest.fn((fn) => fn)

      at mockResizeObserver (node_modules/jsdom-testing-mocks/src/mocks/resize-observer.ts:101:10)
      at Object.<anonymous> (src/components/object-message/object-message.unit.js:8:28)
      at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13)
      at runJest (node_modules/@jest/core/build/runJest.js:404:19)
      at _run10000 (node_modules/@jest/core/build/cli/index.js:320:7)
      at runCLI (node_modules/@jest/core/build/cli/index.js:173:3)

[Request] Add compatibility with other libraries

Based on the name of this library I would have assumed that it was testing-library-agnostic, but it uses jest-specific mocking functions and therefore can't be used with other libraries like Vitest that also use the jsdom environment. I would love to see compatibility expanded, perhaps by providing a configuration option to provide the desired mocking functions or by detecting which testing library is in use.

Export `MockedResizeObserver` class

Overview

Can we please export MockedResizeObserver, similarly to MockedIntersectionObserver? It's helpful when the intention is just polyfilling the API in jsdom, as opposed to actually instantiating the utility methods.

Allow adding more properties to the IntersectionDescription

I am using mockIntersectionObserver.enterNode and the type allows passing a desc which is typed as IntersectionDescription
This type is too basic, some callbacks need to know more about the "entry" such as the intersectionRatio or boundingClientRect

I realise this is probably difficult to do as jsdom does not render actual layouts so a lot of these cannot be calculated easily (or at all)

However you do spread the desc in the enter/leave helper functions,

setNodeState(index, { ...desc, isIntersecting: true });

So I propose that the IntersectionDescription should look a lot more like the TS lib definition with all optionals so they are spread into your callback, then it is up to the developer to set the correct values as the test may require

LocalResizeObserver is not a constructor error in v1.10+

When updating this library I noticed that some of our chart tests (charts use visx and the resize component). They pass fine in 1.9.0 but fail in 1.10+.

TypeError: LocalResizeObserver is not a constructor
 ❯ node_modules/@visx/responsive/lib/components/ParentSize.js:61:20
 ❯ commitHookEffectListMount node_modules/react-dom/cjs/react-dom.development.js:23150:26
 ❯ commitPassiveMountOnFiber node_modules/react-dom/cjs/react-dom.development.js:24931:11
 ❯ commitPassiveMountEffects_complete node_modules/react-dom/cjs/react-dom.development.js:24891:9
 ❯ commitPassiveMountEffects_begin node_modules/react-dom/cjs/react-dom.development.js:24878:7
 ❯ commitPassiveMountEffects node_modules/react-dom/cjs/react-dom.development.js:24866:3
 ❯ flushPassiveEffectsImpl node_modules/react-dom/cjs/react-dom.development.js:27039:3
 ❯ flushPassiveEffects node_modules/react-dom/cjs/react-dom.development.js:26984:14
 ❯ node_modules/react-dom/cjs/react-dom.development.js:26769:9
 ❯ flushActQueue node_modules/react/cjs/react.development.js:2667:24

I have the library configs setup like this:

import { act, cleanup } from '@testing-library/react'
import { afterEach, beforeAll, vi } from 'vitest'
import { configMocks } from 'jsdom-testing-mocks'
import '@testing-library/jest-dom/vitest'

configMocks({ act })

Error with type-fest on build project

Hi.
im using vite for build, and process stoped with this err.
Why could this be? I tried searching on google but didn't find an answer. tell me what could be causing this error and if it's my fault.

node_modules/.pnpm/[email protected][email protected]/node_modules/jsdom-testing-mocks/dist/index.d.ts:1:23 - error TS2305: Module '"type-fest"' has no exported member 'Writable'.

1 import { PartialDeep, Writable, RequireAtLeastOne } from 'type-fest';

Cheers.

Animations aren't captured

Hi I'm not sure if I'm doing anything wrong here or its a bug.
The .getAnimations always returns an empty array.

Accordion.tsx

'use client';

import { v4 as uuidv4 } from 'uuid';
import { Heading } from '@components/Heading/Heading';
import styles from './Accordion.module.scss';
import { useRef, useState } from 'react';

type AccordionProps = {
    label: React.ReactNode;
    children: React.ReactNode;
};

export const Accordion = (props: AccordionProps) => {
    const { label, children } = props;
    const detailsRef = useRef<HTMLDetailsElement>(null);
    const labelRef = useRef<HTMLElement>(null);
    const contentRef = useRef<HTMLDivElement>(null);
    const [animation, setAnimation] = useState<Animation | null>(null);
    const [isClosing, setIsClosing] = useState(false);
    const [isExpanding, setIsExpanding] = useState(false);
    const id = uuidv4();

    const onLabelClick = (e: React.MouseEvent<HTMLElement>): void => {
        e.preventDefault();

        if (!detailsRef.current || !contentRef.current) return;

        detailsRef.current.style.overflow = 'hidden';

        if (isClosing || !detailsRef.current.open) {
            open();
            return;
        }

        if (isExpanding || detailsRef.current.open) {
            shrink();
            return;
        }
    };

    const shrink = () => {
        if (!detailsRef.current || !labelRef.current || !contentRef.current) return;

        // Set the element as "being closing"
        setIsClosing(true);

        // Store the current height of the element
        const startHeight = `${detailsRef.current.offsetHeight}px`;

        // Calculate the height of the summary
        const endHeight = `${detailsRef.current.offsetHeight - contentRef.current.offsetHeight}px`;

        // If there is already an animation running
        if (animation) {
            // Cancel the current animation
            animation.cancel();
        }

        // Start a WAAPI animation
        const currentAnimation = detailsRef.current.animate(
            {
                // Set the keyframes from the startHeight to endHeight
                height: [startHeight, endHeight],
            },
            {
                duration: 400,
                easing: 'ease-in-out',
            },
        );

        // Set animation state
        setAnimation(currentAnimation);

        // When the animation is complete, call onAnimationFinish()
        currentAnimation.onfinish = () => onAnimationFinish(false);

        // If the animation is cancelled, isClosing variable is set to false
        currentAnimation.oncancel = () => setIsClosing(false);
    };

    const open = () => {
        if (!detailsRef.current) return;

        detailsRef.current.style.height = `${detailsRef.current.offsetHeight}px`;

        detailsRef.current.open = true;

        window.requestAnimationFrame(() => expand());
    };

    const expand = () => {
        if (!detailsRef.current || !labelRef.current || !contentRef.current) return;

        // Set the element as "being expanding"
        setIsExpanding(true);

        // Get the current fixed height of the element
        const startHeight = `${detailsRef.current.offsetHeight}px`;

        // Calculate the open height of the element (summary height + content height)
        const endHeight = `${detailsRef.current.offsetHeight + contentRef.current.offsetHeight}px`;

        // If there is already an animation running
        if (animation) {
            // Cancel the current animation
            animation.cancel();
        }

        // Start a WAAPI animation
        const currentAnimation = detailsRef.current.animate(
            {
                // Set the keyframes from the startHeight to endHeight
                height: [startHeight, endHeight],
            },
            {
                duration: 400,
                easing: 'ease-in-out',
            },
        );

        // Set animation state
        setAnimation(currentAnimation);

        // When the animation is complete, call onAnimationFinish()
        currentAnimation.onfinish = () => onAnimationFinish(true);
        // If the animation is cancelled, isExpanding variable is set to false
        currentAnimation.oncancel = () => setIsExpanding(false);
    };

    const onAnimationFinish = (open: boolean) => {
        if (!detailsRef.current || !labelRef.current || !contentRef.current) return;

        // Set the open attribute based on the parameter
        detailsRef.current.open = open;

        labelRef.current.setAttribute('aria-expanded', `${open}`);

        // Clear the stored animation
        setAnimation(null);

        // Reset isClosing & isExpanding
        setIsClosing(false);
        setIsExpanding(false);

        // Remove the overflow hidden and the fixed height
        detailsRef.current.style.height = detailsRef.current.style.overflow = '';
    };

    return (
        <div
            className={`${styles['accordion-wrapper']}`}
            onClick={onLabelClick}
            aria-controls={id}
            aria-expanded="false"
            role="button"
            tabIndex={0}
        >
            <details ref={detailsRef as React.LegacyRef<HTMLDetailsElement>} className={`${styles['accordion']}`}>
                <summary
                    tabIndex={-1}
                    role="decoration"
                    ref={labelRef as React.LegacyRef<HTMLElement>}
                    className={`${styles['label']}`}
                >
                    <Heading className={`${styles['heading']}`} semanticLevel={4} styledLevel={6}>
                        {label}
                    </Heading>
                </summary>

                <div id={id} ref={contentRef as React.LegacyRef<HTMLDivElement>} className={`${styles['content']}`}>
                    {children}
                </div>
            </details>
        </div>
    );
};

Accordion.test.ts

import { screen, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Accordion } from '../Accordion';
import { mockAnimationsApi } from 'jsdom-testing-mocks';

mockAnimationsApi();

describe('Accordion', () => {
    it('should render', () => {
        render(<Accordion label="Label">Content</Accordion>);
        expect(screen.getByText('Label')).toBeInTheDocument();
    });

    it('should open and close', async () => {
        render(<Accordion label="Label">Content</Accordion>);

        const wrapper = screen.getByRole('button');
        const details = screen.getByRole('group');

        expect(details.getAttribute('open')).toBeNull();

        await userEvent.click(wrapper);
        const animation = details.getAnimations(); // <!-- Always empty array -->

        expect(details.getAttribute('open')).not.toBeNull();
    });
});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.