lundi 22 février 2021

How can I test an inline style change based on a returned promise from an image onload using enzyme/jest?

I know this might look like a duplicate but i've been searching and trying various answers for hours. I'm trying to test that opacity of an element changes from 1 to 0 once an image has loaded. My code works fine, it's just the test i'm struggling with as i'm fairly new to testing.

I have tried mocking Image to automatically resolve onload as per the answer here: How to test img.onload using Jest?

I have also tried the original and updated methods in the upvoted answer here: Testing with React's Jest and Enzyme when simulated clicks call a function that calls a promise

Here's the relevant code in my component:

const HeroImage = (props) => {
   const {
      imageTint,
      src,
      srcSm
   } = props;

   HeroImage.propTypes = {
      imageTint: PropTypes.number,
      src: PropTypes.string.isRequired,
      srcSm: PropTypes.string
   };

   const [isLoaded, setIsLoaded] = useState(false);

   useEffect(() => {
      awaitImage();
   }, []);

   const loadImageWithPromise = () => {
      return new Promise(resolve => {
         const image = new Image();
         image.onload = resolve;
         image.src = src;
      });
   };

   const awaitImage = async () => {
      await loadImageWithPromise();
      setIsLoaded(true);
   };

   const initImgOpacity = isLoaded ? 0 : 1;

   return (
      <>
         <div
            className={`
               ${styles.heroBg} 
               ${styles.initImg}`
            }
            data-test="heroImage"
            style={
               {backgroundImage: `linear-gradient(
                  rgba(0, 0, 0, ${imageTint || 0.3}),
                  rgba(0, 0, 0, ${imageTint || 0.3})
               ), url(${srcSm})`,
               opacity: `${initImgOpacity}`}
            }
         >
         </div>
         <div
            className={`${styles.heroBg}`}
            data-test="heroImage"
            style={
               {backgroundImage: `linear-gradient(
                  rgba(0, 0, 0, ${imageTint || 0.3}),
                  rgba(0, 0, 0, ${imageTint || 0.3})
               ), url(${src})`}
            }
         >
         </div>
      </>
   );
};

Here is the relevant test:

describe("HeroImage tests", () => {
   let component;
   const testProps = {
      imageTint: 0.5,
      src,
      srcSm
   };

   beforeEach(() => {
      global.loadImageWithPromise = () => Promise.resolve();
      component = shallow(
         <HeroImage
            imageTint={testProps.imageTint}
            src={testProps.src}
            srcSm={testProps.srcSm}
         />
      );
   });

it("should make the opacity of the small image 0 once the large image has loaded", async () => {
      // global.Image = class {
      //    constructor() {
      //       setTimeout(() => {
      //          this.onload();
      //       }, 10);
      //    }
      // };

      await tick();
      component.update();
      component.setProps({});

      console.error("opacity: ", findByTestAttr(
         component, "heroImage")
         .at(0)
         .props()
         .style
         .opacity
      );

      // setImmediate(() => {
      //    expect(findByTestAttr(
      //       component, "heroImage")
      //       .at(0)
      //       .props()
      //       .style
      //       .opacity).toBe("0");

      //    done();
      // });
   });
});

I know I probably don't need the component.update and setProps there but i've tried everything i can find and think of. No matter what I try, the opacity is always 1. Can anyone see what is wrong with this?

Thanks

Aucun commentaire:

Enregistrer un commentaire