From fa19ba28bf5f7ded1425efb3e8be1e637a12ca26 Mon Sep 17 00:00:00 2001 From: Marvin Frachet Date: Mon, 16 Nov 2020 18:32:30 +0100 Subject: [PATCH] test(gatsby-plugin-image): add test for gatsby-plugin-image browser (#28101) --- .../__tests__/gatsby-image.browser.tsx | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx diff --git a/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx b/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx new file mode 100644 index 0000000000000..eef000aed2637 --- /dev/null +++ b/packages/gatsby-plugin-image/src/components/__tests__/gatsby-image.browser.tsx @@ -0,0 +1,209 @@ +import React from "react" +import { GatsbyImage, ISharpGatsbyImageData } from "../gatsby-image.browser" +import { render, waitFor } from "@testing-library/react" +import * as hooks from "../hooks" + +type GlobalOverride = NodeJS.Global & + typeof global.globalThis & { + GATSBY___IMAGE: boolean + SERVER: boolean + } + +// Prevents terser for bailing because we're not in a babel plugin +jest.mock(`../../../macros/terser.macro`, () => (strs): string => strs.join(``)) + +describe(`GatsbyImage browser`, () => { + let beforeHydrationContent: HTMLDivElement + let image: ISharpGatsbyImageData + + beforeEach(() => { + console.warn = jest.fn() + ;(global as GlobalOverride).SERVER = true + ;(global as GlobalOverride).GATSBY___IMAGE = true + }) + + beforeEach(() => { + image = { + width: 100, + height: 100, + layout: `fluid`, + images: { fallback: { src: `some-src-fallback.jpg` } }, + placeholder: { sources: [] }, + sizes: `192x192`, + backgroundColor: `red`, + } + + beforeHydrationContent = document.createElement(`div`) + beforeHydrationContent.innerHTML = ` +
+
+ +
+ ` + }) + + afterEach(() => { + jest.clearAllMocks() + ;(global as GlobalOverride).SERVER = undefined + ;(global as GlobalOverride).GATSBY___IMAGE = undefined + }) + + it(`shows a suggestion to switch to the new gatsby-image API when available`, async () => { + ;(global as GlobalOverride).GATSBY___IMAGE = false + + const { container } = render( + + ) + + await waitFor(() => container.querySelector(`[data-placeholder-image=""]`)) + + expect(console.warn).toBeCalledWith( + `[gatsby-plugin-image] You're missing out on some cool performance features. Please add "gatsby-plugin-image" to your gatsby-config.js` + ) + }) + + it(`shows nothing when the image props is not passed`, async () => { + process.env.NODE_ENV = `development` + // Allows to get rid of typescript error when not passing image + // This is helpful for user using JavaScript and not getting advent of + // TS types + const GatsbyImageAny = GatsbyImage as React.FC + const { container } = render() + + await waitFor(() => container.querySelector(`[data-placeholder-image=""]`)) + + expect(console.warn).toBeCalledWith( + `[gatsby-plugin-image] Missing image prop` + ) + expect(container.firstChild).toBeNull() + }) + + it(`cleans up the DOM when unmounting`, async () => { + ;(hooks as any).hasNativeLazyLoadSupport = false + + const { container, unmount } = render( + + ) + + await waitFor(() => container.querySelector(`[data-placeholder-image=""]`)) + + unmount() + + expect(container).toMatchInlineSnapshot(`
`) + }) + + it(`does nothing on first server hydration`, async () => { + // In this scenario, + // hasSSRHtml is true and resolved through "beforeHydrationContent" and hydrate: true + // hydrated.current is false and not resolved yet + ;(hooks as any).hasNativeLazyLoadSupport = true + + const { container } = render( + , + { container: beforeHydrationContent, hydrate: true } + ) + + const placeholder = await waitFor(() => + container.querySelector(`[data-placeholder-image=""]`) + ) + const mainImage = container.querySelector(`[data-main-image=""]`) + + expect(placeholder).toBeDefined() + expect(mainImage).toBeDefined() + }) + + it(`relies on native lazy loading when the SSR element exists and that the browser supports native lazy loading`, async () => { + const onStartLoadSpy = jest.fn() + const onLoadSpy = jest.fn() + + // In this scenario, + // hasSSRHtml is true and resolved through "beforeHydrationContent" and hydrate: true + ;(hooks as any).hasNativeLazyLoadSupport = true + ;(hooks as any).storeImageloaded = jest.fn() + + const { container } = render( + , + { container: beforeHydrationContent, hydrate: true } + ) + + const img = await waitFor(() => + container.querySelector(`[data-main-image=""]`) + ) + + img.dispatchEvent(new Event(`load`)) + + expect(onStartLoadSpy).toBeCalledWith({ wasCached: false }) + expect(onLoadSpy).toBeCalled() + expect(hooks.storeImageloaded).toBeCalledWith( + `{"fallback":{"src":"some-src-fallback.jpg"}}` + ) + }) + + it(`relies on intersection observer when the SSR element is not resolved`, async () => { + ;(hooks as any).hasNativeLazyLoadSupport = true + const onStartLoadSpy = jest.fn() + + const { container } = render( + + ) + + await waitFor(() => container.querySelector(`[data-main-image=""]`)) + + expect(onStartLoadSpy).toBeCalledWith({ wasCached: false }) + }) + + it(`relies on intersection observer when browser does not support lazy loading`, async () => { + ;(hooks as any).hasNativeLazyLoadSupport = false + const onStartLoadSpy = jest.fn() + + const { container } = render( + , + { container: beforeHydrationContent, hydrate: true } + ) + + await waitFor(() => container.querySelector(`[data-main-image=""]`)) + + expect(onStartLoadSpy).toBeCalledWith({ wasCached: false }) + }) +})