feat(media): extract workspace package
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import type { ReactNode } from "react"
|
||||
import {
|
||||
cleanup,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
} from "@testing-library/react"
|
||||
import { afterEach, describe, expect, it, vi } from "vitest"
|
||||
import { I18nProvider } from "@workspace/i18n"
|
||||
|
||||
import { MediaProvider } from "./context"
|
||||
import { MediaLibrary } from "./media-library"
|
||||
import { MediaPickerDialog } from "./media-picker-dialog"
|
||||
import type { MediaAdapter, MediaAsset } from "./types"
|
||||
import {
|
||||
defaultMediaReference,
|
||||
getMediaFolderPath,
|
||||
splitMediaName,
|
||||
} from "./utils"
|
||||
|
||||
const imageAsset: MediaAsset = {
|
||||
createdAt: "2026-08-01T00:00:00Z",
|
||||
favorite: false,
|
||||
folderId: null,
|
||||
height: 600,
|
||||
id: "image-1",
|
||||
kind: "image",
|
||||
mimeType: "image/png",
|
||||
name: "product.png",
|
||||
sizeBytes: 1024,
|
||||
url: "/media/product.png",
|
||||
width: 800,
|
||||
}
|
||||
|
||||
function createAdapter(): MediaAdapter {
|
||||
return {
|
||||
createFolder: vi.fn(async ({ name, parentId }) => ({
|
||||
id: name,
|
||||
name,
|
||||
parentId,
|
||||
})),
|
||||
deleteAsset: vi.fn(async () => undefined),
|
||||
deleteFolder: vi.fn(async () => undefined),
|
||||
listAssets: vi.fn(async () => ({
|
||||
assets: [imageAsset],
|
||||
hasNextPage: false,
|
||||
})),
|
||||
listFolders: vi.fn(async () => []),
|
||||
listStorageTargets: vi.fn(async () => [
|
||||
{ id: "local", kind: "local" as const, label: "Local" },
|
||||
]),
|
||||
moveAsset: vi.fn(async () => imageAsset),
|
||||
renameAsset: vi.fn(async () => imageAsset),
|
||||
updateFavorite: vi.fn(async () => imageAsset),
|
||||
updateFolder: vi.fn(async ({ id, name, parentId }) => ({
|
||||
id,
|
||||
name,
|
||||
parentId,
|
||||
})),
|
||||
upload: vi.fn(async () => [imageAsset]),
|
||||
}
|
||||
}
|
||||
|
||||
function MediaTestProvider({
|
||||
adapter,
|
||||
children,
|
||||
}: {
|
||||
adapter: MediaAdapter
|
||||
children: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<I18nProvider locale="en-US">
|
||||
<MediaProvider adapter={adapter}>{children}</MediaProvider>
|
||||
</I18nProvider>
|
||||
)
|
||||
}
|
||||
|
||||
describe("media blocks", () => {
|
||||
afterEach(cleanup)
|
||||
|
||||
it("loads and uploads media only through the provider adapter", async () => {
|
||||
const adapter = createAdapter()
|
||||
const { container } = render(
|
||||
<MediaTestProvider adapter={adapter}>
|
||||
<MediaLibrary />
|
||||
</MediaTestProvider>
|
||||
)
|
||||
|
||||
expect(await screen.findByRole("img", { name: "product.png" })).toBeTruthy()
|
||||
const fileInput =
|
||||
container.querySelector<HTMLInputElement>('input[type="file"]')
|
||||
expect(fileInput).not.toBeNull()
|
||||
|
||||
fireEvent.change(fileInput!, {
|
||||
target: {
|
||||
files: [new File(["image"], "new-image.png", { type: "image/png" })],
|
||||
},
|
||||
})
|
||||
|
||||
await waitFor(() => expect(adapter.upload).toHaveBeenCalledOnce())
|
||||
expect(adapter.upload).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
files: expect.arrayContaining([expect.any(File)]),
|
||||
target: expect.objectContaining({ id: "local" }),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("returns image picker selections with an intrinsic-size reference URL", async () => {
|
||||
const adapter = createAdapter()
|
||||
const onConfirm = vi.fn()
|
||||
render(
|
||||
<MediaTestProvider adapter={adapter}>
|
||||
<MediaPickerDialog
|
||||
open
|
||||
onOpenChange={() => undefined}
|
||||
onConfirm={onConfirm}
|
||||
/>
|
||||
</MediaTestProvider>
|
||||
)
|
||||
|
||||
const image = await screen.findByRole("img", { name: "product.png" })
|
||||
fireEvent.click(image.closest("button")!)
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
(
|
||||
screen.getByRole("button", {
|
||||
name: "Use selected media",
|
||||
}) as HTMLButtonElement
|
||||
).disabled
|
||||
).toBe(false)
|
||||
)
|
||||
fireEvent.click(screen.getByRole("button", { name: "Use selected media" }))
|
||||
|
||||
expect(onConfirm).toHaveBeenCalledWith([
|
||||
expect.objectContaining({ url: "/media/product.png?w=800&h=600" }),
|
||||
])
|
||||
})
|
||||
|
||||
it("builds stable media references and folder paths", () => {
|
||||
expect(defaultMediaReference(imageAsset)).toBe(
|
||||
"/media/product.png?w=800&h=600"
|
||||
)
|
||||
expect(splitMediaName("archive.tar.gz")).toEqual({
|
||||
baseName: "archive.tar",
|
||||
extension: ".gz",
|
||||
})
|
||||
expect(
|
||||
getMediaFolderPath({ id: "child", name: "Shoes", parentId: "root" }, [
|
||||
{ id: "root", name: "Products", parentId: null },
|
||||
{ id: "child", name: "Shoes", parentId: "root" },
|
||||
])
|
||||
).toBe("Products / Shoes")
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user