diff --git a/package.json b/package.json
index ff091ef122f..eda0f5030db 100644
--- a/package.json
+++ b/package.json
@@ -305,7 +305,6 @@
],
"project": "**/*.{ts,tsx}!",
"ignoreDependencies": [
- "@shopify/react-testing",
"react-dom"
]
},
diff --git a/packages/ui-extensions-dev-console/package.json b/packages/ui-extensions-dev-console/package.json
index 8fe1ad363d8..6bc487b6873 100644
--- a/packages/ui-extensions-dev-console/package.json
+++ b/packages/ui-extensions-dev-console/package.json
@@ -23,8 +23,9 @@
"react-transition-group": "^4.4.5"
},
"devDependencies": {
- "@shopify/react-testing": "^5.3.0",
"@shopify/ui-extensions-test-utils": "3.26.0",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@vitejs/plugin-react": "^5.1.4",
diff --git a/packages/ui-extensions-dev-console/src/components/Modal/components/Backdrop/Backdrop.test.tsx b/packages/ui-extensions-dev-console/src/components/Modal/components/Backdrop/Backdrop.test.tsx
index 021674a6a99..5e94bd6c8ac 100644
--- a/packages/ui-extensions-dev-console/src/components/Modal/components/Backdrop/Backdrop.test.tsx
+++ b/packages/ui-extensions-dev-console/src/components/Modal/components/Backdrop/Backdrop.test.tsx
@@ -1,6 +1,6 @@
import {Backdrop} from '.'
import React from 'react'
-import {render} from '@shopify/ui-extensions-test-utils'
+import {render, fireEvent} from '@testing-library/react'
describe('', () => {
const defaultProps = {
@@ -11,8 +11,8 @@ describe('', () => {
describe('onClick()', () => {
test('is called when the backdrop is clicked', () => {
const spy = vi.fn()
- const backdrop = render()
- backdrop.find('div')!.trigger('onClick')
+ const {container} = render()
+ fireEvent.click(container.firstElementChild!)
expect(spy).toHaveBeenCalled()
})
})
@@ -20,8 +20,8 @@ describe('', () => {
describe('onMouseDown', () => {
test('calls setClosing()', () => {
const spy = vi.fn()
- const backdrop = render()
- backdrop.find('div')!.trigger('onMouseDown')
+ const {container} = render()
+ fireEvent.mouseDown(container.firstElementChild!)
expect(spy).toHaveBeenCalledWith(true)
})
})
@@ -29,15 +29,15 @@ describe('', () => {
describe('onMouseUp', () => {
test('calls setClosing()', () => {
const spy = vi.fn()
- const backdrop = render()
- backdrop.find('div')!.trigger('onMouseUp')
+ const {container} = render()
+ fireEvent.mouseUp(container.firstElementChild!)
expect(spy).toHaveBeenCalledWith(false)
})
- test('calls setClosing()', () => {
+ test('calls onClick()', () => {
const spy = vi.fn()
- const backdrop = render()
- backdrop.find('div')!.trigger('onMouseUp')
+ const {container} = render()
+ fireEvent.mouseUp(container.firstElementChild!)
expect(spy).toHaveBeenCalled()
})
})
diff --git a/packages/ui-extensions-dev-console/src/components/Tooltip/tests/Tooltip.test.tsx b/packages/ui-extensions-dev-console/src/components/Tooltip/tests/Tooltip.test.tsx
index d2c8b64155e..237e57f03b3 100644
--- a/packages/ui-extensions-dev-console/src/components/Tooltip/tests/Tooltip.test.tsx
+++ b/packages/ui-extensions-dev-console/src/components/Tooltip/tests/Tooltip.test.tsx
@@ -1,7 +1,6 @@
import {Tooltip} from '../Tooltip'
import React from 'react'
-import {mount} from '@shopify/react-testing'
-import {vi, test} from 'vitest'
+import {render, screen, fireEvent, act} from '@testing-library/react'
const handleClick = vi.fn()
@@ -16,33 +15,33 @@ const ComponentChildComponent = () => (
describe('', () => {
test.each([
- ['onMouseEnter', 'onMouseLeave'],
- ['onFocus', 'onBlur'],
- ])('appears if hovered/focused/etc, hidden otherwise', (showEvent: any, hideEvent: any) => {
- const wrapper = mount()
+ ['mouseEnter', 'mouseLeave'],
+ ['focus', 'blur'],
+ ] as const)('appears if hovered/focused/etc, hidden otherwise', (showEvent, hideEvent) => {
+ render()
- wrapper.act(() => {
- wrapper.find('div')?.trigger(showEvent)
- })
+ const trigger = screen.getByText('test')
- let tooltip = wrapper.find('div', {role: 'tooltip'})
+ act(() => {
+ fireEvent[showEvent](trigger)
+ })
- expect(tooltip).not.toBeNull()
+ expect(screen.queryByRole('tooltip')).toBeInTheDocument()
- wrapper.act(() => {
- wrapper.find('div')?.trigger(hideEvent)
+ act(() => {
+ fireEvent[hideEvent](trigger)
})
- tooltip = wrapper.find('div', {role: 'tooltip'})
-
- expect(tooltip).toBeNull()
+ expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
})
test('hitting enter triggers content onClick', () => {
- const wrapper = mount()
+ render()
+
+ const trigger = screen.getByText('Button').closest('div[tabindex]')!
- wrapper.act(() => {
- wrapper.find('div')?.trigger('onKeyUp', {key: 'Enter'})
+ act(() => {
+ fireEvent.keyUp(trigger, {key: 'Enter'})
})
expect(handleClick).toHaveBeenCalled()
diff --git a/packages/ui-extensions-dev-console/src/sections/Extensions/Extensions.test.tsx b/packages/ui-extensions-dev-console/src/sections/Extensions/Extensions.test.tsx
index fa60f35ef18..00fcd44e9b1 100644
--- a/packages/ui-extensions-dev-console/src/sections/Extensions/Extensions.test.tsx
+++ b/packages/ui-extensions-dev-console/src/sections/Extensions/Extensions.test.tsx
@@ -1,16 +1,15 @@
-import {AppHomeRow, ExtensionRow} from './components'
-
import {Extensions} from './Extensions.js'
import {DefaultProviders} from 'tests/DefaultProviders'
import React from 'react'
import {ExtensionServerClient} from '@shopify/ui-extensions-server-kit'
import {mockExtension} from '@shopify/ui-extensions-server-kit/testing'
import {render, withProviders} from '@shopify/ui-extensions-test-utils'
+import {screen} from '@testing-library/react'
vi.mock('./components', () => ({
- ExtensionRow: () => null,
+ ExtensionRow: ({uuid}: any) =>
,
PostPurchaseRow: () => null,
- AppHomeRow: () => null,
+ AppHomeRow: () => ,
Row: () => null,
}))
@@ -26,21 +25,21 @@ describe('', () => {
})
test('renders a blank slate if there is no data', async () => {
- const container = render(, withProviders(DefaultProviders))
+ render(, withProviders(DefaultProviders))
- expect(container).not.toContainReactComponent('table')
- expect(container).not.toContainReactComponent(AppHomeRow)
- expect(container).not.toContainReactComponent(ExtensionRow)
+ expect(screen.queryByRole('table')).not.toBeInTheDocument()
+ expect(screen.queryByTestId('app-home-row')).not.toBeInTheDocument()
+ expect(screen.queryByTestId(/extension-row-/)).not.toBeInTheDocument()
})
test('renders ', async () => {
const extensions = [mockExtension()]
- const container = render(, withProviders(DefaultProviders), {
+ render(, withProviders(DefaultProviders), {
state: {extensions, store: 'shop1.myshopify.io', app: {url: 'mock.url', title: 'Mock App Title'}},
})
- expect(container).toContainReactComponent(AppHomeRow)
+ expect(screen.getByTestId('app-home-row')).toBeInTheDocument()
})
test('renders an for each Extension', async () => {
@@ -48,12 +47,12 @@ describe('', () => {
const extension2 = mockExtension()
const extensions = [extension1, extension2]
- const container = render(, withProviders(DefaultProviders), {
+ render(, withProviders(DefaultProviders), {
state: {extensions, store: 'shop1.myshopify.io'},
})
- expect(container).toContainReactComponentTimes(ExtensionRow, extensions.length)
- expect(container).toContainReactComponent(ExtensionRow, {uuid: extension1.uuid})
- expect(container).toContainReactComponent(ExtensionRow, {uuid: extension2.uuid})
+ expect(screen.getAllByTestId(/extension-row-/)).toHaveLength(extensions.length)
+ expect(screen.getByTestId(`extension-row-${extension1.uuid}`)).toBeInTheDocument()
+ expect(screen.getByTestId(`extension-row-${extension2.uuid}`)).toBeInTheDocument()
})
})
diff --git a/packages/ui-extensions-dev-console/src/sections/Extensions/components/AppHomeRow/AppHomeRow.test.tsx b/packages/ui-extensions-dev-console/src/sections/Extensions/components/AppHomeRow/AppHomeRow.test.tsx
index 4ebd65c4bb6..5b748dcad2b 100644
--- a/packages/ui-extensions-dev-console/src/sections/Extensions/components/AppHomeRow/AppHomeRow.test.tsx
+++ b/packages/ui-extensions-dev-console/src/sections/Extensions/components/AppHomeRow/AppHomeRow.test.tsx
@@ -1,20 +1,25 @@
import {AppHomeRow} from '.'
-import {PreviewLink, QRCodeModal} from '..'
import {DefaultProviders} from 'tests/DefaultProviders'
-import {Button} from '@/components'
import React from 'react'
-
import {render, withProviders} from '@shopify/ui-extensions-test-utils'
+import {screen, fireEvent} from '@testing-library/react'
-vi.mock('..', () => ({
- NotApplicable: () => null,
- PreviewLink: () => null,
- QRCodeModal: () => null,
- Row: (props: any) => props.children,
+const {QRCodeModalMock, PreviewLinkMock} = vi.hoisted(() => ({
+ QRCodeModalMock: vi.fn(),
+ PreviewLinkMock: vi.fn(),
}))
-vi.mock('@/components', () => ({
- Button: (props: any) => props.children,
+vi.mock('..', () => ({
+ NotApplicable: () => null,
+ PreviewLink: (props: any) => {
+ PreviewLinkMock(props)
+ return null
+ },
+ QRCodeModal: (props: any) => {
+ QRCodeModalMock(props)
+ return
+ },
+ Row: ({children}: any) => {children}
,
}))
describe('', () => {
@@ -22,55 +27,60 @@ describe('', () => {
app: {url: 'mock.url', title: 'Mock App Title'},
}
+ beforeEach(() => {
+ QRCodeModalMock.mockClear()
+ PreviewLinkMock.mockClear()
+ })
+
test('renders a , closed by default', () => {
- const container = render(, withProviders(DefaultProviders), {state: defaultState})
+ render(, withProviders(DefaultProviders), {state: defaultState})
- expect(container).toContainReactComponent(QRCodeModal, {code: undefined})
+ expect(QRCodeModalMock).toHaveBeenLastCalledWith(expect.objectContaining({code: undefined}))
})
test('Opens and closes the ', () => {
- const container = render(, withProviders(DefaultProviders), {state: defaultState})
+ render(, withProviders(DefaultProviders), {state: defaultState})
- container.act(() => {
- container.find(Button)?.trigger('onClick')
- })
+ fireEvent.click(screen.getByText('View mobile'))
- expect(container).toContainReactComponent(QRCodeModal, {
- code: {
- url: defaultState.app.url,
- type: 'home',
- title: defaultState.app.title,
- },
- })
+ expect(QRCodeModalMock).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ code: {
+ url: defaultState.app.url,
+ type: 'home',
+ title: defaultState.app.title,
+ },
+ }),
+ )
- container.act(() => {
- container.find(QRCodeModal)?.trigger('onClose')
- })
+ fireEvent.click(screen.getByTestId('qr-code-modal'))
- expect(container).toContainReactComponent(QRCodeModal, {code: undefined})
+ expect(QRCodeModalMock).toHaveBeenLastCalledWith(expect.objectContaining({code: undefined}))
})
test("renders a with the resource url set to the app's handle if the surface has been set to 'admin'", () => {
const appState = {
app: {url: 'mock.url', title: 'Mock App Title', handle: 'my-app-handle'},
}
- const container = render(, withProviders(DefaultProviders), {
+ render(, withProviders(DefaultProviders), {
state: appState,
client: {options: {surface: 'admin'}},
})
- expect(container).toContainReactComponent(PreviewLink, {resourceUrl: '/admin/apps/my-app-handle'})
+ expect(PreviewLinkMock).toHaveBeenLastCalledWith(
+ expect.objectContaining({resourceUrl: '/admin/apps/my-app-handle'}),
+ )
})
test("renders a without a resource url if the surface has not been set to 'admin'", () => {
const appState = {
app: {url: 'mock.url', title: 'Mock App Title', handle: 'my-app-handle'},
}
- const container = render(, withProviders(DefaultProviders), {
+ render(, withProviders(DefaultProviders), {
state: appState,
client: {options: {surface: 'checkout'}},
})
- expect(container).toContainReactComponent(PreviewLink, {resourceUrl: undefined})
+ expect(PreviewLinkMock).toHaveBeenLastCalledWith(expect.objectContaining({resourceUrl: undefined}))
})
})
diff --git a/packages/ui-extensions-dev-console/src/sections/Extensions/components/ExtensionRow/ExtensionRow.test.tsx b/packages/ui-extensions-dev-console/src/sections/Extensions/components/ExtensionRow/ExtensionRow.test.tsx
index abf19f75713..973b9ced99f 100644
--- a/packages/ui-extensions-dev-console/src/sections/Extensions/components/ExtensionRow/ExtensionRow.test.tsx
+++ b/packages/ui-extensions-dev-console/src/sections/Extensions/components/ExtensionRow/ExtensionRow.test.tsx
@@ -1,19 +1,24 @@
import {ExtensionRow} from '.'
-import {QRCodeModal} from '..'
import {DefaultProviders} from 'tests/DefaultProviders'
-import {Button} from '@/components'
import React from 'react'
-
import {render, withProviders} from '@shopify/ui-extensions-test-utils'
+import {screen, fireEvent} from '@testing-library/react'
import {mockExtension} from '@shopify/ui-extensions-server-kit/testing'
+const {QRCodeModalMock} = vi.hoisted(() => ({
+ QRCodeModalMock: vi.fn(),
+}))
+
vi.mock('./components', () => ({
PreviewLinks: () => null,
}))
vi.mock('..', () => ({
- QRCodeModal: () => null,
- Row: (props: any) => props.children,
+ QRCodeModal: (props: any) => {
+ QRCodeModalMock(props)
+ return
+ },
+ Row: ({children, ...props}: any) => {children}
,
Status: () => null,
}))
@@ -53,115 +58,91 @@ describe('', () => {
],
}
+ beforeEach(() => {
+ QRCodeModalMock.mockClear()
+ })
+
test('renders a , closed by default', () => {
- const container = render(, withProviders(DefaultProviders), {state: defaultState})
+ render(, withProviders(DefaultProviders), {state: defaultState})
- expect(container).toContainReactComponent(QRCodeModal, {code: undefined})
+ expect(QRCodeModalMock).toHaveBeenLastCalledWith(expect.objectContaining({code: undefined}))
})
- test('renders a to open the QRCodeModal for a POS UI extension', () => {
- const container = render(
- ,
- withProviders(DefaultProviders),
- {
- state: defaultState,
- },
- )
+ test('renders a button to open the QRCodeModal for a POS UI extension', () => {
+ render(, withProviders(DefaultProviders), {
+ state: defaultState,
+ })
- expect(container).toContainReactComponent(Button, {id: 'showQRCodeModalButton'})
+ expect(screen.getByText('View mobile')).toBeInTheDocument()
})
- test('renders a to open the QRCodeModal for a legacy Admin extension', () => {
- const container = render(
- ,
- withProviders(DefaultProviders),
- {
- state: defaultState,
- },
- )
+ test('renders a button to open the QRCodeModal for a legacy Admin extension', () => {
+ render(, withProviders(DefaultProviders), {
+ state: defaultState,
+ })
- expect(container).toContainReactComponent(Button, {id: 'showQRCodeModalButton'})
+ expect(screen.getByText('View mobile')).toBeInTheDocument()
})
- test('renders a to open the QRCodeModal for a legacy POS extension', () => {
- const container = render(
- ,
- withProviders(DefaultProviders),
- {
- state: defaultState,
- },
- )
+ test('renders a button to open the QRCodeModal for a legacy POS extension', () => {
+ render(, withProviders(DefaultProviders), {
+ state: defaultState,
+ })
- expect(container).toContainReactComponent(Button, {id: 'showQRCodeModalButton'})
+ expect(screen.getByText('View mobile')).toBeInTheDocument()
})
- test('does not render a to open the QRCodeModal for a legacy Post purchase extension', () => {
- const container = render(
+ test('does not render a button to open the QRCodeModal for a legacy Post purchase extension', () => {
+ render(
,
withProviders(DefaultProviders),
- {
- state: defaultState,
- },
+ {state: defaultState},
)
- expect(container).not.toContainReactComponent(Button, {id: 'showQRCodeModalButton'})
+ expect(screen.queryByText('View mobile')).not.toBeInTheDocument()
})
- test('does not render a to open the QRCodeModal for a legacy Checkout extension', () => {
- const container = render(
- ,
- withProviders(DefaultProviders),
- {
- state: defaultState,
- },
- )
+ test('does not render a button to open the QRCodeModal for a legacy Checkout extension', () => {
+ render(, withProviders(DefaultProviders), {
+ state: defaultState,
+ })
- expect(container).not.toContainReactComponent(Button, {id: 'showQRCodeModalButton'})
+ expect(screen.queryByText('View mobile')).not.toBeInTheDocument()
})
- test('does not render a to open the QRCodeModal for a Checkout UI extension', () => {
- const container = render(
- ,
- withProviders(DefaultProviders),
- {
- state: defaultState,
- },
- )
+ test('does not render a button to open the QRCodeModal for a Checkout UI extension', () => {
+ render(, withProviders(DefaultProviders), {
+ state: defaultState,
+ })
- expect(container).not.toContainReactComponent(Button, {id: 'showQRCodeModalButton'})
+ expect(screen.queryByText('View mobile')).not.toBeInTheDocument()
})
- test('does not render a to open the QRCodeModal for an Admin UI extension', () => {
- const container = render(
- ,
- withProviders(DefaultProviders),
- {
- state: defaultState,
- },
- )
+ test('does not render a button to open the QRCodeModal for an Admin UI extension', () => {
+ render(, withProviders(DefaultProviders), {
+ state: defaultState,
+ })
- expect(container).not.toContainReactComponent(Button, {id: 'showQRCodeModalButton'})
+ expect(screen.queryByText('View mobile')).not.toBeInTheDocument()
})
test('Opens and closes the ', () => {
- const container = render(, withProviders(DefaultProviders), {state: defaultState})
-
- container.act(() => {
- container.find(Button, {id: 'showQRCodeModalButton'})?.trigger('onClick')
- })
-
- expect(container).toContainReactComponent(QRCodeModal, {
- code: {
- url: legacyAdminExtension.development.root.url,
- type: legacyAdminExtension.surface,
- title: legacyAdminExtension.handle,
- },
- })
+ render(, withProviders(DefaultProviders), {state: defaultState})
+
+ fireEvent.click(screen.getByText('View mobile'))
+
+ expect(QRCodeModalMock).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ code: {
+ url: legacyAdminExtension.development.root.url,
+ type: legacyAdminExtension.surface,
+ title: legacyAdminExtension.handle,
+ },
+ }),
+ )
- container.act(() => {
- container.find(QRCodeModal)?.trigger('onClose')
- })
+ fireEvent.click(screen.getByTestId('qr-code-modal'))
- expect(container).toContainReactComponent(QRCodeModal, {code: undefined})
+ expect(QRCodeModalMock).toHaveBeenLastCalledWith(expect.objectContaining({code: undefined}))
})
})
diff --git a/packages/ui-extensions-dev-console/src/sections/Extensions/components/QRCodeModal/QRCodeModal.test.tsx b/packages/ui-extensions-dev-console/src/sections/Extensions/components/QRCodeModal/QRCodeModal.test.tsx
index d01f63c21c8..ace0a495144 100644
--- a/packages/ui-extensions-dev-console/src/sections/Extensions/components/QRCodeModal/QRCodeModal.test.tsx
+++ b/packages/ui-extensions-dev-console/src/sections/Extensions/components/QRCodeModal/QRCodeModal.test.tsx
@@ -1,14 +1,27 @@
import {QRCodeModal} from './QRCodeModal'
import {DefaultProviders} from 'tests/DefaultProviders'
-import {Modal} from '@/components/Modal'
import React from 'react'
-import {QRCodeCanvas as QRCode} from 'qrcode.react'
import {mockApp, mockExtension} from '@shopify/ui-extensions-server-kit/testing'
import {render, withProviders} from '@shopify/ui-extensions-test-utils'
-vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(null)
+const {ModalMock, QRCodeMock} = vi.hoisted(() => ({
+ ModalMock: vi.fn(),
+ QRCodeMock: vi.fn(),
+}))
-vi.mock('@/components/Modal', () => ({Modal: (props: any) => props.children}))
+vi.mock('@/components/Modal', () => ({
+ Modal: (props: any) => {
+ ModalMock(props)
+ return props.children
+ },
+}))
+
+vi.mock('qrcode.react', () => ({
+ QRCodeCanvas: (props: any) => {
+ QRCodeMock(props)
+ return
+ },
+}))
describe('QRCodeModal', () => {
const defaultProps = {
@@ -20,74 +33,76 @@ describe('QRCodeModal', () => {
},
}
+ beforeEach(() => {
+ ModalMock.mockClear()
+ QRCodeMock.mockClear()
+ })
+
test('Renders closed if code is undefined', async () => {
const app = mockApp()
const store = 'example.com'
const extension = mockExtension()
- expect(
- render(, withProviders(DefaultProviders), {
- state: {app, store, extensions: [extension]},
- }),
- ).toContainReactComponent(Modal, {open: false})
+ render(, withProviders(DefaultProviders), {
+ state: {app, store, extensions: [extension]},
+ })
+
+ expect(ModalMock).toHaveBeenLastCalledWith(expect.objectContaining({open: false}))
})
- test('Renders open if code is undefined', async () => {
+ test('Renders open if code is defined', async () => {
const app = mockApp()
const store = 'example.com'
const extension = mockExtension()
- expect(
- render(, withProviders(DefaultProviders), {
- state: {app, store, extensions: [extension]},
- }),
- ).toContainReactComponent(Modal, {open: true, width: 'small'})
+ render(, withProviders(DefaultProviders), {
+ state: {app, store, extensions: [extension]},
+ })
+
+ expect(ModalMock).toHaveBeenLastCalledWith(expect.objectContaining({open: true, width: 'small'}))
})
test('renders QRCode for pos', async () => {
const app = mockApp()
const store = 'example.com'
const extension = mockExtension()
- const container = render(
+
+ render(
,
withProviders(DefaultProviders),
- {
- state: {app, store, extensions: [extension]},
- },
+ {state: {app, store, extensions: [extension]}},
)
- expect(container).toContainReactComponent(QRCode, {
- value: `com.shopify.pos://pos-ui-extensions?url=${defaultProps.code.url}`,
- })
+ expect(QRCodeMock).toHaveBeenLastCalledWith(
+ expect.objectContaining({value: `com.shopify.pos://pos-ui-extensions?url=${defaultProps.code.url}`}),
+ )
})
test('renders QRCode for app home', async () => {
const app = mockApp()
const store = 'example.com'
const extension = mockExtension()
- const container = render(, withProviders(DefaultProviders), {
+
+ render(, withProviders(DefaultProviders), {
state: {app, store, extensions: [extension]},
})
- expect(container).toContainReactComponent(QRCode, {
- value: app.mobileUrl,
- })
+ expect(QRCodeMock).toHaveBeenLastCalledWith(expect.objectContaining({value: app.mobileUrl}))
})
test('renders QRCode for mobile', async () => {
const app = mockApp()
const store = 'example.com'
const extension = mockExtension()
- const container = render(
+
+ render(
,
withProviders(DefaultProviders),
- {
- state: {app, store, extensions: [extension]},
- },
+ {state: {app, store, extensions: [extension]}},
)
- expect(container).toContainReactComponent(QRCode, {
- value: `https://${store}/admin/extensions-dev/mobile?url=${defaultProps.code.url}`,
- })
+ expect(QRCodeMock).toHaveBeenLastCalledWith(
+ expect.objectContaining({value: `https://${store}/admin/extensions-dev/mobile?url=${defaultProps.code.url}`}),
+ )
})
})
diff --git a/packages/ui-extensions-dev-console/tests/setup.ts b/packages/ui-extensions-dev-console/tests/setup.ts
index c8fb1a35cd3..834184042f5 100644
--- a/packages/ui-extensions-dev-console/tests/setup.ts
+++ b/packages/ui-extensions-dev-console/tests/setup.ts
@@ -1,4 +1,4 @@
-import '@shopify/react-testing/matchers'
+import '@testing-library/jest-dom/vitest'
Object.defineProperty(window, 'matchMedia', {
writable: true,
diff --git a/packages/ui-extensions-server-kit/package.json b/packages/ui-extensions-server-kit/package.json
index 2dccdf4cf76..cc17c4adedc 100644
--- a/packages/ui-extensions-server-kit/package.json
+++ b/packages/ui-extensions-server-kit/package.json
@@ -52,8 +52,7 @@
}
},
"devDependencies": {
- "@shopify/react-testing": "^5.3.0",
- "@shopify/ui-extensions-test-utils": "3.26.0",
+ "@testing-library/react": "^16.3.2",
"@types/react": "^18.2.0",
"@vitejs/plugin-react": "^5.1.4",
"jsdom": "^25.0.0",
diff --git a/packages/ui-extensions-server-kit/src/context/ExtensionServerProvider.test.tsx b/packages/ui-extensions-server-kit/src/context/ExtensionServerProvider.test.tsx
index 04af2420f6b..915ed1bb907 100644
--- a/packages/ui-extensions-server-kit/src/context/ExtensionServerProvider.test.tsx
+++ b/packages/ui-extensions-server-kit/src/context/ExtensionServerProvider.test.tsx
@@ -2,19 +2,15 @@ import {ExtensionServerProvider} from './ExtensionServerProvider'
import {useExtensionServerContext} from '../hooks'
import {createConnectedAction} from '../state'
import {mockApp, mockExtension} from '../testing'
+import React from 'react'
import {beforeEach, afterEach, expect} from 'vitest'
-import {renderHook, withProviders} from '@shopify/ui-extensions-test-utils'
+import {renderHook, act} from '@testing-library/react'
-// Cast provider to any to avoid type conflicts between required props and ProviderComponent
-const TestProvider = ExtensionServerProvider as Parameters[0]
-
-// Create a custom mock WebSocket implementation to avoid using jest-websocket-mock
class MockWebSocketServer {
clients: MockWebSocket[] = []
messages: any[] = []
connect(socket: MockWebSocket) {
- // Make socket connection active
this.clients.push(socket)
socket.readyState = 1
socket.onopen?.({} as Event)
@@ -31,7 +27,6 @@ class MockWebSocketServer {
}
close() {
- // Close all socket connections
this.clients.forEach((client) => {
client.readyState = 3
client.onclose?.({} as CloseEvent)
@@ -66,7 +61,6 @@ class MockWebSocket implements Partial {
}
this.eventListeners[type].add(listener)
- // Map standard event handlers to addEventListener
if (type === 'open' && this.onopen === null) {
this.onopen = (event) => {
this.eventListeners.open.forEach((listener) => listener(event))
@@ -116,27 +110,27 @@ class MockWebSocket implements Partial {
}
}
-// Set up mock socket server and prepare for test environment
let mockSocketServer: MockWebSocketServer
let originalWebSocket: typeof WebSocket
-// Clear sockets before each test
+function createWrapper(options: {connection: {url?: string}}) {
+ return function Wrapper({children}: {children: React.ReactNode}) {
+ return {children}
+ }
+}
+
beforeEach(() => {
mockSocketServer = new MockWebSocketServer()
- // Store original WebSocket and replace with our mock
originalWebSocket = globalThis.WebSocket
- // Mock WebSocket global
globalThis.WebSocket = function (url: string) {
const socket = new MockWebSocket(url, mockSocketServer)
return socket as unknown as WebSocket
} as unknown as typeof WebSocket
})
-// Restore original WebSocket after each test
afterEach(() => {
- // Restore the original WebSocket
globalThis.WebSocket = originalWebSocket
})
@@ -145,17 +139,17 @@ describe('ExtensionServerProvider tests', () => {
test('creates a new ExtensionServerClient instance', async () => {
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
- const wrapper = renderHook(useExtensionServerContext, withProviders(TestProvider), {options})
+ const {result} = renderHook(useExtensionServerContext, {wrapper: createWrapper(options)})
- expect(wrapper.result.client).toBeDefined()
+ expect(result.current.client).toBeDefined()
})
test('does not start a new connection if an empty url is passed', async () => {
const options = {connection: {}}
- const wrapper = renderHook(useExtensionServerContext, withProviders(TestProvider), {options})
+ const {result} = renderHook(useExtensionServerContext, {wrapper: createWrapper(options)})
- expect(wrapper.result.client.connection).toBeUndefined()
+ expect(result.current.client.connection).toBeUndefined()
})
})
@@ -163,18 +157,13 @@ describe('ExtensionServerProvider tests', () => {
test('starts a new connection by calling connect', async () => {
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
- const wrapper = renderHook(useExtensionServerContext, withProviders(TestProvider), {
- options: {
- connection: {url: ''},
- },
+ const {result} = renderHook(useExtensionServerContext, {
+ wrapper: createWrapper({connection: {url: ''}}),
})
- // Execute the connect action
- wrapper.act(({connect}) => connect(options))
+ act(() => result.current.connect(options))
- // We won't rely on mockSocketServer.clients since the WebSocket mock might not be correctly added
- // Just check that the connection object exists
- expect(wrapper.result.client.connection).toBeDefined()
+ expect(result.current.client.connection).toBeDefined()
})
})
@@ -184,13 +173,14 @@ describe('ExtensionServerProvider tests', () => {
const app = mockApp()
const extension = mockExtension()
const payload = {app, extensions: [extension], store: 'test-store.com'}
- const wrapper = renderHook(useExtensionServerContext, withProviders(TestProvider), {options})
- wrapper.act(({dispatch}) => {
- dispatch({type: 'connected', payload})
+ const {result} = renderHook(useExtensionServerContext, {wrapper: createWrapper(options)})
+
+ act(() => {
+ result.current.dispatch({type: 'connected', payload})
})
- expect(wrapper.result.state).toStrictEqual({
+ expect(result.current.state).toStrictEqual({
app,
extensions: [extension],
store: 'test-store.com',
@@ -204,16 +194,14 @@ describe('ExtensionServerProvider tests', () => {
const extension = mockExtension()
const data = {app, store: 'test-store.com', extensions: [extension]}
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
- const wrapper = renderHook(useExtensionServerContext, withProviders(TestProvider), {options})
- // Since we can't be sure the socket connection works properly in the test environment
- // Initialize data through the dispatch action instead
- wrapper.act(({dispatch}) => {
- dispatch(createConnectedAction(data))
+ const {result} = renderHook(useExtensionServerContext, {wrapper: createWrapper(options)})
+
+ act(() => {
+ result.current.dispatch(createConnectedAction(data))
})
- // Verify state has been updated
- expect(wrapper.result.state).toEqual({
+ expect(result.current.state).toEqual({
app,
extensions: [extension],
store: 'test-store.com',
@@ -226,20 +214,18 @@ describe('ExtensionServerProvider tests', () => {
const update = {...extension, version: 'v2'}
const data = {app, store: 'test-store.com', extensions: [extension]}
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
- const wrapper = renderHook(useExtensionServerContext, withProviders(TestProvider), {options})
- // Initialize state with connected data
- wrapper.act(({dispatch}) => {
- dispatch(createConnectedAction(data))
+ const {result} = renderHook(useExtensionServerContext, {wrapper: createWrapper(options)})
+
+ act(() => {
+ result.current.dispatch(createConnectedAction(data))
})
- // Update through dispatch rather than socket message
- wrapper.act(({dispatch}) => {
- dispatch(createConnectedAction({...data, extensions: [update]}))
+ act(() => {
+ result.current.dispatch(createConnectedAction({...data, extensions: [update]}))
})
- // Verify state has been updated
- expect(wrapper.result.state).toEqual({
+ expect(result.current.state).toEqual({
app,
extensions: [update],
store: 'test-store.com',
@@ -251,21 +237,19 @@ describe('ExtensionServerProvider tests', () => {
const extension = mockExtension()
const data = {app, store: 'test-store.com', extensions: [extension]}
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
- const wrapper = renderHook(useExtensionServerContext, withProviders(TestProvider), {options})
- // Initialize state with connected data
- wrapper.act(({dispatch}) => {
- dispatch(createConnectedAction(data))
+ const {result} = renderHook(useExtensionServerContext, {wrapper: createWrapper(options)})
+
+ act(() => {
+ result.current.dispatch(createConnectedAction(data))
})
- // Verify state has been updated - the extension should still exist
- expect(wrapper.result.state.extensions.length).toBe(1)
- expect(wrapper.result.state.extensions[0].uuid).toBe(extension.uuid)
+ expect(result.current.state.extensions.length).toBe(1)
+ expect(result.current.state.extensions[0].uuid).toBe(extension.uuid)
})
test('persists focus data to the state', async () => {
const app = mockApp()
- // Create extension with development object that includes focused property
const extension = {
...mockExtension(),
development: {
@@ -275,15 +259,14 @@ describe('ExtensionServerProvider tests', () => {
}
const data = {app, store: 'test-store.com', extensions: [extension]}
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
- const wrapper = renderHook(useExtensionServerContext, withProviders(TestProvider), {options})
- // Initialize state with connected data
- wrapper.act(({dispatch}) => {
- dispatch(createConnectedAction(data))
+ const {result} = renderHook(useExtensionServerContext, {wrapper: createWrapper(options)})
+
+ act(() => {
+ result.current.dispatch(createConnectedAction(data))
})
- // Update state to focus the extension
- wrapper.act(({dispatch}) => {
+ act(() => {
const focusedExtension = {
...extension,
development: {
@@ -291,17 +274,15 @@ describe('ExtensionServerProvider tests', () => {
focused: true,
},
}
- dispatch(createConnectedAction({...data, extensions: [focusedExtension]}))
+ result.current.dispatch(createConnectedAction({...data, extensions: [focusedExtension]}))
})
- // Verify extension is now focused
- const [updatedExtension] = wrapper.result.state.extensions
+ const [updatedExtension] = result.current.state.extensions
expect(updatedExtension.development.focused).toBe(true)
})
test('persists unfocus data to the state', async () => {
const app = mockApp()
- // Set extension as initially focused
const extension = {
...mockExtension(),
development: {
@@ -312,15 +293,14 @@ describe('ExtensionServerProvider tests', () => {
const data = {app, store: 'test-store.com', extensions: [extension]}
const options = {connection: {url: 'ws://example-host.com:8000/extensions/'}}
- const wrapper = renderHook(useExtensionServerContext, withProviders(TestProvider), {options})
- // Initialize state with connected data
- wrapper.act(({dispatch}) => {
- dispatch(createConnectedAction(data))
+ const {result} = renderHook(useExtensionServerContext, {wrapper: createWrapper(options)})
+
+ act(() => {
+ result.current.dispatch(createConnectedAction(data))
})
- // Update state to unfocus the extension
- wrapper.act(({dispatch}) => {
+ act(() => {
const unfocusedExtension = {
...extension,
development: {
@@ -328,11 +308,10 @@ describe('ExtensionServerProvider tests', () => {
focused: false,
},
}
- dispatch(createConnectedAction({...data, extensions: [unfocusedExtension]}))
+ result.current.dispatch(createConnectedAction({...data, extensions: [unfocusedExtension]}))
})
- // Verify extension is now unfocused
- const [updatedExtension] = wrapper.result.state.extensions
+ const [updatedExtension] = result.current.state.extensions
expect(updatedExtension.development.focused).toBe(false)
})
})
diff --git a/packages/ui-extensions-test-utils/package.json b/packages/ui-extensions-test-utils/package.json
index 96f96e11c22..1a6b4f4a2a6 100644
--- a/packages/ui-extensions-test-utils/package.json
+++ b/packages/ui-extensions-test-utils/package.json
@@ -28,7 +28,7 @@
}
},
"devDependencies": {
- "@shopify/react-testing": "^5.3.0",
+ "@testing-library/react": "^16.3.2",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"react": "^18.2.0",
diff --git a/packages/ui-extensions-test-utils/src/render.tsx b/packages/ui-extensions-test-utils/src/render.tsx
index b6bb2a3a249..2e797aada46 100644
--- a/packages/ui-extensions-test-utils/src/render.tsx
+++ b/packages/ui-extensions-test-utils/src/render.tsx
@@ -1,10 +1,14 @@
import React from 'react'
-import {mount} from '@shopify/react-testing'
+import {render as rtlRender, type RenderResult} from '@testing-library/react'
-export function render(
- element: React.ReactElement,
+export type {RenderResult}
+
+export function render(
+ element: React.ReactElement,
Providers: React.ComponentType> = ({children}) => <>{children}>,
options: Omit = {} as TProviderProps,
-) {
- return mount({element})
+): RenderResult {
+ return rtlRender(element, {
+ wrapper: ({children}) => React.createElement(Providers, options as TProviderProps, children),
+ })
}
diff --git a/packages/ui-extensions-test-utils/src/renderHook.tsx b/packages/ui-extensions-test-utils/src/renderHook.tsx
index 51aa319da3f..954534153bf 100644
--- a/packages/ui-extensions-test-utils/src/renderHook.tsx
+++ b/packages/ui-extensions-test-utils/src/renderHook.tsx
@@ -1,47 +1,14 @@
import React from 'react'
-import {mount} from '@shopify/react-testing'
+import {renderHook as rtlRenderHook, type RenderHookResult} from '@testing-library/react'
-export interface HookWrapper {
- result: T
+export type {RenderHookResult}
- /**
- * Use to wrap calls to stateful hook methods:
- *
- * ```ts
- * const hook = testHook(() => useToggle(false))
- * expect(hook.result.value).toBe(false)
- * hook.act(() => hook.result.toggle())
- * expect(hook.result.value).toBe(true)
- * ```
- **/
- act(callback: (currentResult: T) => TR): TR
- forceUpdate(): void
-}
-
-function MountHook({callback}: {callback(): void}) {
- callback()
- return null
-}
-
-export function renderHook(
- hook: () => T,
- Providers: React.ComponentType> = ({children}) => <>{children}>,
- options: Omit = {} as TP,
-) {
- const hookResult: HookWrapper = {} as HookWrapper
- const wrapper = mount(
-
- (hookResult.result = hook())} />
- ,
- )
-
- return Object.assign, HookWrapper>(hookResult, {
- result: hookResult.result,
- forceUpdate() {
- return wrapper.forceUpdate()
- },
- act(callback) {
- return wrapper.act(() => callback(hookResult.result))
- },
+export function renderHook(
+ hook: () => TResult,
+ Providers: React.ComponentType> = ({children}) => <>{children}>,
+ options: Omit = {} as TProviderProps,
+): RenderHookResult {
+ return rtlRenderHook(hook, {
+ wrapper: ({children}) => React.createElement(Providers, options as TProviderProps, children),
})
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 610c5178558..ef3cdc80312 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -739,12 +739,15 @@ importers:
specifier: ^4.4.5
version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
devDependencies:
- '@shopify/react-testing':
- specifier: ^5.3.0
- version: 5.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@shopify/ui-extensions-test-utils':
specifier: 3.26.0
version: link:../ui-extensions-test-utils
+ '@testing-library/jest-dom':
+ specifier: ^6.9.1
+ version: 6.9.1
+ '@testing-library/react':
+ specifier: ^16.3.2
+ version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.12))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@types/react':
specifier: 18.3.12
version: 18.3.12
@@ -766,12 +769,9 @@ importers:
packages/ui-extensions-server-kit:
devDependencies:
- '@shopify/react-testing':
- specifier: ^5.3.0
- version: 5.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@shopify/ui-extensions-test-utils':
- specifier: 3.26.0
- version: link:../ui-extensions-test-utils
+ '@testing-library/react':
+ specifier: ^16.3.2
+ version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.12))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@types/react':
specifier: 18.3.12
version: 18.3.12
@@ -793,9 +793,9 @@ importers:
packages/ui-extensions-test-utils:
devDependencies:
- '@shopify/react-testing':
- specifier: ^5.3.0
- version: 5.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@testing-library/react':
+ specifier: ^16.3.2
+ version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.12))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@types/react':
specifier: 18.3.12
version: 18.3.12
@@ -841,6 +841,9 @@ packages:
'@actions/io@1.1.3':
resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==}
+ '@adobe/css-tools@4.4.4':
+ resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
+
'@alcalzone/ansi-tokenize@0.1.3':
resolution: {integrity: sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==}
engines: {node: '>=14.13.1'}
@@ -2806,10 +2809,6 @@ packages:
resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
- '@jest/types@26.6.2':
- resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==}
- engines: {node: '>= 10.14.2'}
-
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
@@ -3657,14 +3656,6 @@ packages:
react: ^18.0.0
react-dom: ^18.0.0
- '@shopify/react-testing@5.4.0':
- resolution: {integrity: sha512-qlu6NUnMK9Mq/1lu6MIvZMSKT/AtXnHGyuu148DMshVF8d7E5+JDtgTVGeKKV8nnjL5cly4K91dyPVt6fHq5yA==}
- engines: {node: '>=18.12.0'}
- deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
- peerDependencies:
- react: '>=18.0.0 <19.0.0'
- react-dom: '>=16.8.0 <19.0.0'
-
'@shopify/theme-check-common@3.23.0':
resolution: {integrity: sha512-g3aR7vw/hulA/ugGG8htRyoqiPsBhxEkQzVGivX5L1ymPYGFxt6ZwdhkpNLUEw+rWwP6fqnUoSjipbPtjYZjIQ==}
@@ -3691,11 +3682,6 @@ packages:
'@shopify/toml-patch@0.3.0':
resolution: {integrity: sha512-ruv2FT17FW3CfWx8jXEyfx3xZDdo22PDaFQ9R7QYOovXQbgQCIKY+5QjGVBA7jsyLm+Wa2ngVANmt7MS0M/g+w==}
- '@shopify/useful-types@5.3.0':
- resolution: {integrity: sha512-8BlaedSqR7ssQAhCsa88K+l2eIImSBlTLlnf7/dlqSlMSEqGVUf+O8oPsnyy9Oh3aDMt+rD4Q5JPOo984Cbz7Q==}
- engines: {node: '>=18.12.0'}
- deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
-
'@sinclair/typebox@0.34.48':
resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==}
@@ -3927,6 +3913,29 @@ packages:
resolution: {integrity: sha512-ID7fosbc50TbT0MK0EG12O+gAP3W3Aa/Pz4DaTtQtEvlc9Odaqi0de+xuZ7Li2GtK4HzEX7IuRWS/JmZLksR3Q==}
engines: {node: '>=14'}
+ '@testing-library/dom@10.4.1':
+ resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.9.1':
+ resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.3.2':
+ resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': 18.3.12
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@theguild/federation-composition@0.21.3':
resolution: {integrity: sha512-+LlHTa4UbRpZBog3ggAxjYIFvdfH3UMvvBUptur19TMWkqU4+n3GmN+mDjejU+dyBXIG27c25RsiQP1HyvM99g==}
engines: {node: '>=18'}
@@ -3960,6 +3969,9 @@ packages:
'@types/archiver@5.3.2':
resolution: {integrity: sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw==}
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@@ -4023,15 +4035,6 @@ packages:
'@types/http-errors@2.0.5':
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
- '@types/istanbul-lib-coverage@2.0.6':
- resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
-
- '@types/istanbul-lib-report@3.0.3':
- resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
-
- '@types/istanbul-reports@3.0.4':
- resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
-
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@@ -4131,12 +4134,6 @@ packages:
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
- '@types/yargs-parser@21.0.3':
- resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
-
- '@types/yargs@15.0.20':
- resolution: {integrity: sha512-KIkX+/GgfFitlASYCGoSF+T4XRXhOubJLhkLVtSfsRTe9jWMmuM2g28zQ41BtPTG7TRBb2xHW+LCNVE9QR/vsg==}
-
'@typescript-eslint/eslint-plugin@8.56.1':
resolution: {integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -4507,6 +4504,9 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
aria-query@5.3.2:
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
engines: {node: '>= 0.4'}
@@ -5155,6 +5155,9 @@ packages:
resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
cssstyle@4.6.0:
resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==}
engines: {node: '>=18'}
@@ -5309,6 +5312,10 @@ packages:
resolution: {integrity: sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==}
engines: {node: '>=4'}
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
destr@1.2.2:
resolution: {integrity: sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==}
@@ -5343,10 +5350,6 @@ packages:
engines: {node: '>= 4.0.0'}
hasBin: true
- diff-sequences@26.6.2:
- resolution: {integrity: sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==}
- engines: {node: '>= 10.14.2'}
-
diff@3.5.1:
resolution: {integrity: sha512-Z3u54A8qGyqFOSr2pk0ijYs8mOE9Qz8kTvtKeBI+upoG9j04Sq+oI7W8zAJiQybDcESET8/uIdHzs0p3k4fZlw==}
engines: {node: '>=0.3.1'}
@@ -5367,6 +5370,12 @@ packages:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
dom-helpers@5.2.1:
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
@@ -6764,22 +6773,10 @@ packages:
engines: {node: '>=10'}
hasBin: true
- jest-diff@26.6.2:
- resolution: {integrity: sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==}
- engines: {node: '>= 10.14.2'}
-
jest-diff@30.2.0:
resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
- jest-get-type@26.3.0:
- resolution: {integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==}
- engines: {node: '>= 10.14.2'}
-
- jest-matcher-utils@26.6.2:
- resolution: {integrity: sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==}
- engines: {node: '>= 10.14.2'}
-
jiti@2.6.1:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true
@@ -7056,6 +7053,10 @@ packages:
resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
engines: {node: '>=12'}
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
macaddress@0.5.3:
resolution: {integrity: sha512-vGBKTA+jwM4KgjGZ+S/8/Mkj9rWzePyGY6jManXPGhiWu63RYwW8dKPyk5koP+8qNVhPhHgFa1y/MJ4wrjsNrg==}
@@ -7838,9 +7839,9 @@ packages:
engines: {node: '>=14'}
hasBin: true
- pretty-format@26.6.2:
- resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==}
- engines: {node: '>= 10'}
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
pretty-format@30.2.0:
resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==}
@@ -7973,12 +7974,6 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
- react-reconciler@0.28.0:
- resolution: {integrity: sha512-sGIHDOpgVjRYgsi8NgosDnbkDvvkYFFSF900ZUhUw0+lSBEA5n76TcKFaVkfYMIuYm+7W6mT8Q673DLBfuTxcQ==}
- engines: {node: '>=0.10.0'}
- peerDependencies:
- react: ^18.1.0
-
react-reconciler@0.29.2:
resolution: {integrity: sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==}
engines: {node: '>=0.10.0'}
@@ -8267,9 +8262,6 @@ packages:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
- scheduler@0.22.0:
- resolution: {integrity: sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ==}
-
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@@ -9434,6 +9426,8 @@ snapshots:
'@actions/io@1.1.3': {}
+ '@adobe/css-tools@4.4.4': {}
+
'@alcalzone/ansi-tokenize@0.1.3':
dependencies:
ansi-styles: 6.2.3
@@ -12215,14 +12209,6 @@ snapshots:
dependencies:
'@sinclair/typebox': 0.34.48
- '@jest/types@26.6.2':
- dependencies:
- '@types/istanbul-lib-coverage': 2.0.6
- '@types/istanbul-reports': 3.0.4
- '@types/node': 18.19.70
- '@types/yargs': 15.0.20
- chalk: 4.1.2
-
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -13238,15 +13224,6 @@ snapshots:
react-fast-compare: 3.2.2
react-transition-group: 4.4.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
- '@shopify/react-testing@5.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
- dependencies:
- '@shopify/useful-types': 5.3.0
- jest-matcher-utils: 26.6.2
- react: 18.3.1
- react-dom: 18.3.1(react@18.3.1)
- react-is: 18.3.1
- react-reconciler: 0.28.0(react@18.3.1)
-
'@shopify/theme-check-common@3.23.0':
dependencies:
'@shopify/liquid-html-parser': 2.9.0
@@ -13318,8 +13295,6 @@ snapshots:
'@shopify/toml-patch@0.3.0': {}
- '@shopify/useful-types@5.3.0': {}
-
'@sinclair/typebox@0.34.48': {}
'@sindresorhus/is@5.6.0': {}
@@ -13668,6 +13643,36 @@ snapshots:
'@teppeis/multimaps@3.0.0': {}
+ '@testing-library/dom@10.4.1':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/runtime': 7.28.6
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ picocolors: 1.1.1
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.9.1':
+ dependencies:
+ '@adobe/css-tools': 4.4.4
+ aria-query: 5.3.2
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ picocolors: 1.1.1
+ redent: 3.0.0
+
+ '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.12))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.28.6
+ '@testing-library/dom': 10.4.1
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.12
+ '@types/react-dom': 18.3.7(@types/react@18.3.12)
+
'@theguild/federation-composition@0.21.3(graphql@16.10.0)':
dependencies:
constant-case: 3.0.4
@@ -13713,6 +13718,8 @@ snapshots:
dependencies:
'@types/readdir-glob': 1.1.5
+ '@types/aria-query@5.0.4': {}
+
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.29.0
@@ -13796,16 +13803,6 @@ snapshots:
'@types/http-errors@2.0.5': {}
- '@types/istanbul-lib-coverage@2.0.6': {}
-
- '@types/istanbul-lib-report@3.0.3':
- dependencies:
- '@types/istanbul-lib-coverage': 2.0.6
-
- '@types/istanbul-reports@3.0.4':
- dependencies:
- '@types/istanbul-lib-report': 3.0.3
-
'@types/json-schema@7.0.15': {}
'@types/lodash@4.17.19': {}
@@ -13898,12 +13895,6 @@ snapshots:
dependencies:
'@types/node': 18.19.70
- '@types/yargs-parser@21.0.3': {}
-
- '@types/yargs@15.0.20':
- dependencies:
- '@types/yargs-parser': 21.0.3
-
'@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
@@ -14353,6 +14344,10 @@ snapshots:
argparse@2.0.1: {}
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
aria-query@5.3.2: {}
array-back@3.1.0: {}
@@ -15083,6 +15078,8 @@ snapshots:
source-map-js: 1.2.1
optional: true
+ css.escape@1.5.1: {}
+
cssstyle@4.6.0:
dependencies:
'@asamuzakjp/css-color': 3.2.0
@@ -15212,6 +15209,8 @@ snapshots:
dependency-graph@1.0.0: {}
+ dequal@2.0.3: {}
+
destr@1.2.2: {}
destr@2.0.5: {}
@@ -15236,8 +15235,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- diff-sequences@26.6.2: {}
-
diff@3.5.1: {}
diff@4.0.4: {}
@@ -15252,6 +15249,10 @@ snapshots:
dependencies:
esutils: 2.0.3
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
dom-helpers@5.2.1:
dependencies:
'@babel/runtime': 7.28.6
@@ -16939,13 +16940,6 @@ snapshots:
filelist: 1.0.5
picocolors: 1.1.1
- jest-diff@26.6.2:
- dependencies:
- chalk: 4.1.2
- diff-sequences: 26.6.2
- jest-get-type: 26.3.0
- pretty-format: 26.6.2
-
jest-diff@30.2.0:
dependencies:
'@jest/diff-sequences': 30.0.1
@@ -16953,15 +16947,6 @@ snapshots:
chalk: 4.1.2
pretty-format: 30.2.0
- jest-get-type@26.3.0: {}
-
- jest-matcher-utils@26.6.2:
- dependencies:
- chalk: 4.1.2
- jest-diff: 26.6.2
- jest-get-type: 26.3.0
- pretty-format: 26.6.2
-
jiti@2.6.1: {}
jju@1.4.0: {}
@@ -17264,6 +17249,8 @@ snapshots:
luxon@3.7.2: {}
+ lz-string@1.5.0: {}
+
macaddress@0.5.3: {}
magic-string@0.30.21:
@@ -18080,11 +18067,10 @@ snapshots:
prettier@3.8.1: {}
- pretty-format@26.6.2:
+ pretty-format@27.5.1:
dependencies:
- '@jest/types': 26.6.2
ansi-regex: 5.0.1
- ansi-styles: 4.3.0
+ ansi-styles: 5.2.0
react-is: 17.0.2
pretty-format@30.2.0:
@@ -18229,12 +18215,6 @@ snapshots:
react-is@18.3.1: {}
- react-reconciler@0.28.0(react@18.3.1):
- dependencies:
- loose-envify: 1.4.0
- react: 18.3.1
- scheduler: 0.22.0
-
react-reconciler@0.29.2(react@18.3.1):
dependencies:
loose-envify: 1.4.0
@@ -18588,10 +18568,6 @@ snapshots:
dependencies:
xmlchars: 2.2.0
- scheduler@0.22.0:
- dependencies:
- loose-envify: 1.4.0
-
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0