diff --git a/packages/hydrogen/src/components/ExternalVideo/tests/ExternalVideo.test.tsx b/packages/hydrogen/src/components/ExternalVideo/tests/ExternalVideo.test.tsx deleted file mode 100644 index fcd7e600dc..0000000000 --- a/packages/hydrogen/src/components/ExternalVideo/tests/ExternalVideo.test.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import {mount} from '@shopify/react-testing'; -import {ExternalVideo} from '../index.js'; -import {getExternalVideoData} from '../../../utilities/tests/media.js'; - -describe('', () => { - it('renders an iframe element with sensible defaults', () => { - const video = getExternalVideoData(); - const component = mount(); - - expect(component).toContainReactComponent('iframe', { - src: video.embedUrl, - id: video.id, - allow: - 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture', - allowFullScreen: true, - frameBorder: '0', - }); - }); - - it('allows defaults to be overridden', () => { - const component = mount( - - ); - - expect(component).toContainReactComponent('iframe', { - id: 'hello', - allow: 'autoplay', - allowFullScreen: false, - frameBorder: '1', - }); - }); - - it('includes options in the iframe src when the `options` prop is provided', () => { - const options = { - color: 'red', - autoplay: true, - }; - const component = mount( - - ); - - expect(component).toContainReactComponent('iframe', { - src: 'https://www.youtube.com/embed/a2YSgfwXc9c?&color=red&autoplay=true', - }); - }); - - it('allows passthrough props', () => { - const component = mount( - - ); - - expect(component).toContainReactComponent('iframe', { - className: 'fancy', - }); - }); - - describe(`throws when necessary props aren't passed`, () => { - it(`data.embedUrl`, () => { - // to silence the test runner's console.error from being called - const consoleSpy = jest - .spyOn(console, 'error') - .mockImplementation(() => {}); - expect(() => mount()).toThrow(); - expect(console.error).toHaveBeenCalled(); - consoleSpy.mockRestore(); - }); - }); -}); diff --git a/packages/hydrogen/src/components/ExternalVideo/tests/ExternalVideo.vitest.tsx b/packages/hydrogen/src/components/ExternalVideo/tests/ExternalVideo.vitest.tsx index 7a8fd24d53..354df4a4a5 100644 --- a/packages/hydrogen/src/components/ExternalVideo/tests/ExternalVideo.vitest.tsx +++ b/packages/hydrogen/src/components/ExternalVideo/tests/ExternalVideo.vitest.tsx @@ -84,9 +84,8 @@ describe('', () => { it(`throws when 'data.embedUrl' isn't passed`, () => { // to silence the test runner's console.error from being called - const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + vi.spyOn(console, 'error').mockImplementation(() => {}); expect(() => render()).toThrow(); expect(console.error).toHaveBeenCalled(); - consoleSpy.mockRestore(); }); }); diff --git a/packages/hydrogen/src/components/Image/tests/Image.test.tsx b/packages/hydrogen/src/components/Image/tests/Image.vitest.tsx similarity index 57% rename from packages/hydrogen/src/components/Image/tests/Image.test.tsx rename to packages/hydrogen/src/components/Image/tests/Image.vitest.tsx index 4a70efe0d8..d8d8d44eb3 100644 --- a/packages/hydrogen/src/components/Image/tests/Image.test.tsx +++ b/packages/hydrogen/src/components/Image/tests/Image.vitest.tsx @@ -1,59 +1,56 @@ import React from 'react'; +import {vi} from 'vitest'; +import {render, screen} from '@testing-library/react'; import {Image} from '../index.js'; -import {mount} from '@shopify/react-testing'; import {getPreviewImage} from '../../../utilities/tests/media.js'; import * as utilities from '../../../utilities/index.js'; describe('', () => { - let consoleWarnSpy: jest.SpyInstance; - beforeEach(() => { - consoleWarnSpy = jest.spyOn(console, 'warn'); - consoleWarnSpy.mockImplementation(() => {}); - }); - afterEach(() => { - consoleWarnSpy.mockRestore(); + beforeAll(() => { + vi.spyOn(console, 'error').mockImplementation(() => {}); }); + describe('Shopify image data', () => { it('renders an `img` element', () => { - const image = getPreviewImage(); - const {url: src, altText, id, width, height} = image; - - const component = mount(); - - expect(component).toContainReactComponent('img', { - src, - alt: altText, - id, - width, - height, - loading: 'lazy', - }); + const previewImage = getPreviewImage(); + const {url: src, altText, id, width, height} = previewImage; + render(); + + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', src); + expect(image).toHaveAttribute('id', id); + expect(image).toHaveAttribute('alt', altText); + expect(image).toHaveAttribute('width', `${width}`); + expect(image).toHaveAttribute('height', `${height}`); + expect(image).toHaveAttribute('loading', 'lazy'); }); it('renders an `img` element with provided `id`', () => { - const image = getPreviewImage(); + const previewImage = getPreviewImage(); const id = 'catImage'; + render(); - const component = mount(); + const image = screen.getByRole('img'); - expect(component).toContainReactComponent('img', { - id, - }); + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('id', id); }); it('renders an `img` element with provided `loading` value', () => { - const image = getPreviewImage(); + const previewImage = getPreviewImage(); const loading = 'eager'; + render(); - const component = mount(); + const image = screen.getByRole('img'); - expect(component).toContainReactComponent('img', { - loading, - }); + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('loading', loading); }); it('renders an `img` with `width` and `height` values', () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', }); const options = {scale: 2 as const}; @@ -61,23 +58,22 @@ describe('', () => { width: 200, height: 100, }; - jest - .spyOn(utilities, 'getShopifyImageDimensions') - .mockReturnValue(mockDimensions); - const component = mount(); + vi.spyOn(utilities, 'getShopifyImageDimensions').mockReturnValue( + mockDimensions + ); - expect(component).toContainReactComponent('img', { - width: mockDimensions.width, - height: mockDimensions.height, - }); + render(); + + const image = screen.getByRole('img'); - // @ts-expect-error clear the mock that was created earlier - utilities.getShopifyImageDimensions.mockRestore(); + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('width', `${mockDimensions.width}`); + expect(image).toHaveAttribute('height', `${mockDimensions.height}`); }); it('renders an `img` element without `width` and `height` attributes when invalid dimensions are provided', () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', }); const options = {scale: 2 as const}; @@ -86,22 +82,22 @@ describe('', () => { height: null, }; - jest - .spyOn(utilities, 'getShopifyImageDimensions') - .mockReturnValue(mockDimensions); - const component = mount(); + vi.spyOn(utilities, 'getShopifyImageDimensions').mockReturnValue( + mockDimensions + ); + + render(); - const img = component.find('img'); - expect(img?.prop('width')).toBeUndefined(); - expect(img?.prop('height')).toBeUndefined(); + const image = screen.getByRole('img'); - // @ts-expect-error This was mocked out and needs to be restored - utilities.getShopifyImageDimensions.mockRestore(); + expect(image).toBeInTheDocument(); + expect(image).not.toHaveAttribute('width'); + expect(image).not.toHaveAttribute('height'); }); describe('Loaders', () => { it('calls `shopifyImageLoader()` when no `loader` prop is provided', () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', }); @@ -110,39 +106,44 @@ describe('', () => { const options = {width: 100, height: 200, scale: 2 as const}; - const shopifyImageLoaderSpy = jest + const shopifyImageLoaderSpy = vi .spyOn(utilities, 'shopifyImageLoader') .mockReturnValue(transformedSrc); - const component = mount(); + render(); expect(shopifyImageLoaderSpy).toHaveBeenCalledWith({ - src: image.url, + src: previewImage.url, ...options, }); - expect(component).toContainReactComponent('img', { - src: transformedSrc, - }); - // @ts-expect-error This was mocked out and needs to be restored - utilities.shopifyImageLoader.mockRestore(); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', transformedSrc); }); }); it('allows passthrough props', () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', }); - const component = mount( - Fancy image + render( + Fancy image ); - expect(component).toContainReactComponent('img', { - className: 'fancyImage', - id: '123', - alt: 'Fancy image', - }); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveClass('fancyImage'); + expect(image).toHaveAttribute('id', '123'); + expect(image).toHaveAttribute('alt', 'Fancy image'); }); it('generates a default srcset', () => { @@ -151,17 +152,18 @@ describe('', () => { const expectedSrcset = sizes .map((size) => `${mockUrl}?width=${size} ${size}w`) .join(', '); - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: mockUrl, width: 2560, height: 2560, }); - const component = mount(); + render(); - expect(component).toContainReactComponent('img', { - srcSet: expectedSrcset, - }); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('srcSet', expectedSrcset); }); it('generates a default srcset up to the image height and width', () => { @@ -170,157 +172,177 @@ describe('', () => { const expectedSrcset = sizes .map((size) => `${mockUrl}?width=${size} ${size}w`) .join(', '); - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: mockUrl, width: 832, height: 832, }); - const component = mount(); + render(); - expect(component).toContainReactComponent('img', { - srcSet: expectedSrcset, - }); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('srcSet', expectedSrcset); }); it(`uses scale to multiply the srcset width but not the element width, and when crop is missing, does not include height in srcset`, () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', width: 500, height: 500, }); - const component = mount( - - ); + render(); - expect(component).toContainReactComponent('img', { - width: 500, - height: 500, + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute( + 'srcSet', // height is not applied if there is no crop // width is not doulbe of the passed width, but instead double of the value in 'sizes_array' / '[number]w' - srcSet: `${image.url}?width=704 352w`, - }); + `${previewImage.url}?width=704 352w` + ); + expect(image).toHaveAttribute('width', '500'); + expect(image).toHaveAttribute('height', '500'); }); it(`uses scale to multiply the srcset width but not the element width, and when crop is there, includes height in srcset`, () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', width: 500, height: 500, }); - const component = mount( + render( ); - expect(component).toContainReactComponent('img', { - width: 500, - height: 250, + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute( + 'srcSet', // height is the aspect ratio (of width + height) * srcSet width, so in this case it should be half of width - srcSet: `${image.url}?width=704&height=352&crop=bottom 352w`, - }); + `${previewImage.url}?width=704&height=352&crop=bottom 352w` + ); + expect(image).toHaveAttribute('width', '500'); + expect(image).toHaveAttribute('height', '250'); }); it(`uses scale to multiply the srcset width but not the element width, and when crop is there, includes height in srcset using data.width / data.height for the aspect ratio`, () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', width: 500, height: 500, }); - const component = mount( - + render( + ); - expect(component).toContainReactComponent('img', { - width: 500, - height: 500, + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute( + 'srcSet', // height is the aspect ratio (of data.width + data.height) * srcSet width, so in this case it should be the same as width - srcSet: `${image.url}?width=704&height=704&crop=bottom 352w`, - }); + `${previewImage.url}?width=704&height=704&crop=bottom 352w` + ); + expect(image).toHaveAttribute('width', '500'); + expect(image).toHaveAttribute('height', '500'); }); it(`uses scale to multiply the srcset width but not the element width, and when crop is there, calculates height based on aspect ratio in srcset`, () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', width: 500, height: 1000, }); - const component = mount( - + render( + ); - expect(component).toContainReactComponent('img', { - width: 500, - height: 1000, + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute( + 'srcSet', // height is the aspect ratio (of data.width + data.height) * srcSet width, so in this case it should be double the width - srcSet: `${image.url}?width=704&height=1408&crop=bottom 352w`, - }); + `${previewImage.url}?width=704&height=1408&crop=bottom 352w` + ); + expect(image).toHaveAttribute('width', '500'); + expect(image).toHaveAttribute('height', '1000'); }); it(`should pass through width (as an inline prop) when it's a string, and use the first size in the size array for the URL width`, () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', width: 100, height: 100, }); - const component = mount(); + render(); - expect(component).toContainReactComponent('img', { - width: '100%', - src: `${image.url}?width=352`, - height: undefined, // make sure height isn't NaN - }); + const image = screen.getByRole('img'); + + console.log(image.getAttribute('srcSet')); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', `${previewImage.url}?width=352`); + expect(image).toHaveAttribute('width', '100%'); + expect(image).not.toHaveAttribute('height'); }); it(`should pass through width (as part of loaderOptions) when it's a string, and use the first size in the size array for the URL width`, () => { - const image = getPreviewImage({ + const previewImage = getPreviewImage({ url: 'https://cdn.shopify.com/someimage.jpg', width: 100, height: 100, }); - const component = mount( - - ); + render(); - expect(component).toContainReactComponent('img', { - width: '100%', - src: `${image.url}?width=352`, - height: undefined, // make sure height isn't NaN - }); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', `${previewImage.url}?width=352`); + expect(image).toHaveAttribute('width', '100%'); + expect(image).not.toHaveAttribute('height'); }); }); describe('External image', () => { it('renders an `img` element', () => { const {url: src, altText, id, width, height} = getPreviewImage(); - const component = mount( + + render( {altText} ); - expect(component).toContainReactComponent('img', { - src, - alt: altText, - id, - width, - height, - loading: 'lazy', - }); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', src); + expect(image).toHaveAttribute('id', id); + expect(image).toHaveAttribute('alt', altText); + expect(image).toHaveAttribute('width', `${width}`); + expect(image).toHaveAttribute('height', `${height}`); + expect(image).toHaveAttribute('loading', 'lazy'); }); it('renders an `img` element with provided `loading` value', () => { const {url: src, id, width, height} = getPreviewImage(); const loading = 'eager'; - const component = mount( + + render( ', () => { /> ); - expect(component).toContainReactComponent('img', { - loading, - }); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('loading', loading); }); describe('Width and height checks', () => { - let consoleErrorSpy: jest.SpyInstance; - - beforeEach(() => { - consoleErrorSpy = jest.spyOn(console, 'error'); - consoleErrorSpy.mockImplementation(() => {}); - }); - - afterEach(() => { - consoleErrorSpy.mockRestore(); - }); - it('throws an error when the `width` is set to zero', () => { const {url: src, id} = getPreviewImage(); const width = 0; const height = 100; expect(() => { - mount( + render( ); }).toThrowError( @@ -368,7 +380,7 @@ describe('', () => { const height = 0; expect(() => { - mount( + render( ); }).toThrowError( @@ -387,14 +399,14 @@ describe('', () => { url: 'https://cdn.shopify.com/someimage.jpg', }); const transformedSrc = 'https://cdn.shopify.com/someimage_100x200@2x.jpg'; - const loaderMock = jest.fn().mockReturnValue(transformedSrc); + const loaderMock = vi.fn().mockReturnValue(transformedSrc); const loaderOptions = { width: 100, height: 200, scale: 2 as const, }; - const component = mount( + render( ', () => { /> ); - expect(component).toContainReactComponent('img', { - src: transformedSrc, - }); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', transformedSrc); expect(loaderMock).toHaveBeenCalledWith({ src, @@ -418,7 +431,7 @@ describe('', () => { it('allows passthrough props', () => { const {url: src, width, height} = getPreviewImage(); - const component = mount( + render( Fancy image', () => { /> ); - expect(component).toContainReactComponent('img', { - className: 'fancyImage', - id: '123', - alt: 'Fancy image', - }); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('id', '123'); + expect(image).toHaveAttribute('alt', 'Fancy image'); + expect(image).toHaveClass('fancyImage'); }); it('generates a srcset when a loader and a widths prop are provided', () => { @@ -466,7 +480,7 @@ describe('', () => { ) .join(', '); - const component = mount( + render( ', () => { /> ); - expect(component).toContainReactComponent('img', { - srcSet: expectedSrcset, - }); + const image = screen.getByRole('img'); + + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('srcSet', expectedSrcset); }); }); - // eslint-disable-next-line jest/expect-expect it.skip(`typescript types`, () => { // this test is actually just using //@ts-expect-error as the assertion, and don't need to execute in order to have TS validation on them // I don't love this idea, but at the moment I also don't have other great ideas for how to easily test our component TS types diff --git a/packages/hydrogen/src/components/ShopPayButton/tests/ShopPayButton.client.vitest.tsx b/packages/hydrogen/src/components/ShopPayButton/tests/ShopPayButton.client.vitest.tsx index 43c5086dc1..a4289849f9 100644 --- a/packages/hydrogen/src/components/ShopPayButton/tests/ShopPayButton.client.vitest.tsx +++ b/packages/hydrogen/src/components/ShopPayButton/tests/ShopPayButton.client.vitest.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {vi, type SpyInstance} from 'vitest'; +import {vi} from 'vitest'; import {render} from '@testing-library/react'; import {ShopifyTestProviders} from '../../../utilities/tests/provider-helpers.js'; import {