-
-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): hopefully enable vitest test-utils
- Loading branch information
Showing
7 changed files
with
958 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from "./match-media.js"; | ||
export * from "./resize-observer.js"; | ||
export * from "./timers.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { type MockInstance, vi } from "vitest"; | ||
import { | ||
createMatchMediaSpy, | ||
type MatchMediaChangeViewport, | ||
} from "../mocks/match-media-implementation.js"; | ||
import { matchDesktop, type MatchMediaMatcher } from "../mocks/match-media.js"; | ||
|
||
/** | ||
* @example Default Behavior | ||
* ```tsx | ||
* import { matchPhone, render, spyOnMatchMedia } from "@react-md/core/test-utils"; | ||
* | ||
* const matchMedia = spyOnMatchMedia(); | ||
* render(<Test />); | ||
* | ||
* // expect desktop results | ||
* | ||
* matchMedia.changeViewport(matchPhone); | ||
* // expect phone results | ||
* ``` | ||
* | ||
* @example Set Default Media | ||
* ```tsx | ||
* import { matchPhone, render, spyOnMatchMedia } from "@react-md/core/test-utils"; | ||
* | ||
* const matchMedia = spyOnMatchMedia(matchPhone); | ||
* render(<Test />); | ||
* | ||
* // expect phone results | ||
* ``` | ||
* | ||
* @since 6.0.0 | ||
*/ | ||
export function spyOnMatchMedia( | ||
defaultMatch: MatchMediaMatcher = matchDesktop | ||
): MockInstance<typeof window.matchMedia> & MatchMediaChangeViewport { | ||
return createMatchMediaSpy(vi.spyOn(window, "matchMedia"), defaultMatch); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { afterEach, vi } from "vitest"; | ||
import { resizeObserverManager } from "../../useResizeObserver.js"; | ||
import { | ||
type SetupResizeObserverMockOptions, | ||
ResizeObserverMock, | ||
} from "../mocks/ResizeObserver.js"; | ||
|
||
/** | ||
* Initializes the `ResizeObserverMock` to be used for tests. | ||
* | ||
* @example Main Usage | ||
* ```tsx | ||
* import { render, screen } from "@react-md/core/test-utils"; | ||
* import { | ||
* cleanupResizeObserverAfterEach, | ||
* setupResizeObserverMock, | ||
* } from "test-utils/jest-globals"; | ||
* import { useResizeObserver } from "@react-md/core/useResizeObserver"; | ||
* import { useCallback, useState } from "react"; | ||
* | ||
* function ExampleComponent() { | ||
* const [size, setSize] = useState({ height: 0, width: 0 }); | ||
* const ref = useResizeObserver({ | ||
* onUpdate: useCallback((entry) => { | ||
* setSize({ | ||
* height: entry.contentRect.height, | ||
* width: entry.contentRect.width, | ||
* }); | ||
* }, []), | ||
* }); | ||
* | ||
* return ( | ||
* <> | ||
* <div data-testid="size">{JSON.stringify(size)}</div> | ||
* <div data-testid="resize-target" ref={ref} /> | ||
* </> | ||
* ); | ||
* } | ||
* | ||
* cleanupResizeObserverAfterEach(); | ||
* | ||
* describe("ExampleComponent", () => { | ||
* it("should do stuff", () => { | ||
* const observer = setupResizeObserverMock(); | ||
* render(<ExampleComponent />); | ||
* | ||
* const size = screen.getByTestId("size"); | ||
* const resizeTarget = screen.getByTestId("resize-target"); | ||
* | ||
* // jsdom sets all element sizes to 0 by default | ||
* expect(size).toHaveTextContent(JSON.stringify({ height: 0, width: 0 })); | ||
* | ||
* // you can trigger with a custom change | ||
* act(() => { | ||
* observer.resizeElement(resizeTarget, { height: 100, width: 100 }); | ||
* }); | ||
* expect(size).toHaveTextContent(JSON.stringify({ height: 100, width: 100 })); | ||
* | ||
* // or you can mock the `getBoundingClientRect` result | ||
* jest.spyOn(resizeTarget, "getBoundingClientRect").mockReturnValue({ | ||
* ...document.body.getBoundingClientRect(), | ||
* height: 200, | ||
* width: 200, | ||
* }); | ||
* | ||
* act(() => { | ||
* observer.resizeElement(resizeTarget); | ||
* }); | ||
* expect(size).toHaveTextContent(JSON.stringify({ height: 200, width: 200 })); | ||
* }); | ||
* }); | ||
* ``` | ||
* | ||
* @since 6.0.0 | ||
*/ | ||
export function setupResizeObserverMock( | ||
options: SetupResizeObserverMockOptions = {} | ||
): ResizeObserverMock { | ||
const { raf, manager = resizeObserverManager } = options; | ||
|
||
const resizeObserver = new ResizeObserverMock((entries) => { | ||
if (raf) { | ||
window.cancelAnimationFrame(manager.frame); | ||
manager.frame = window.requestAnimationFrame(() => { | ||
manager.handleResizeEntries(entries); | ||
}); | ||
} else { | ||
manager.handleResizeEntries(entries); | ||
} | ||
}); | ||
manager.sharedObserver = resizeObserver; | ||
return resizeObserver; | ||
} | ||
|
||
/** | ||
* @see {@link setupResizeObserverMock} | ||
* @since 6.0.0 | ||
*/ | ||
export function cleanupResizeObserverAfterEach(restoreAllMocks = true): void { | ||
afterEach(() => { | ||
resizeObserverManager.frame = 0; | ||
resizeObserverManager.subscriptions = new Map(); | ||
resizeObserverManager.sharedObserver = undefined; | ||
|
||
if (restoreAllMocks) { | ||
vi.restoreAllMocks(); | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import "@testing-library/jest-dom/vitest"; | ||
|
||
import { INTERACTION_CONFIG } from "../../interaction/config.js"; | ||
import { TRANSITION_CONFIG } from "../../transition/config.js"; | ||
|
||
// @ts-expect-error I do not have globals enabled | ||
beforeEach(() => { | ||
// set the mode to `none` in tests since ripples require | ||
// `getBoundingClientRect()` to create correct CSS. You'll either see warnings | ||
// in the console around invalid css values or `NaN`. | ||
INTERACTION_CONFIG.mode = "none"; | ||
|
||
// disable transitions in tests since it just makes it more difficult | ||
TRANSITION_CONFIG.disabled = true; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { type MockInstance, vi } from "vitest"; | ||
|
||
/** | ||
* @since 6.0.0 | ||
*/ | ||
export type RafSpy = MockInstance<typeof requestAnimationFrame>; | ||
|
||
/** | ||
* @example | ||
* ```ts | ||
* import { testImmediateRaf } from "@react-md/core/test-utils/jest-globals"; | ||
* | ||
* describe("some test suite", () => { | ||
* it("should test something with requestAnimationFrame", () => { | ||
* const raf = testImmediateRaf(); | ||
* | ||
* // do some testing with requestAnimationFrame | ||
* | ||
* // reset to original at the end of the test | ||
* raf.mockRestore() | ||
* }); | ||
* }); | ||
* ``` | ||
* | ||
* @example Automatic Cleanup | ||
* ```ts | ||
* import { testImmediateRaf } from "@react-md/core/test-utils/jest-globals"; | ||
* | ||
* afterEach(() => { | ||
* jest.restoreAllMocks(); | ||
* }); | ||
* | ||
* describe("some test suite", () => { | ||
* it("should test something with requestAnimationFrame", () => { | ||
* const raf = testImmediateRaf(); | ||
* | ||
* // do some testing with requestAnimationFrame | ||
* }); | ||
* }); | ||
* ``` | ||
* | ||
* @since 6.0.0 | ||
*/ | ||
export const testImmediateRaf = (): RafSpy => | ||
vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => { | ||
cb(0); | ||
return 0; | ||
}); |
Oops, something went wrong.