test(image-loader): add unit tests

This commit is contained in:
Eric Liu 2025-03-15 18:17:16 -07:00
commit cb1f5d5a12
3 changed files with 193 additions and 22 deletions

View file

@ -1,22 +0,0 @@
<script lang="ts">
import { ImageLoader } from "carbon-components-svelte";
let loading = false;
let loaded = false;
let error = false;
</script>
<ImageLoader
bind:loading
bind:loaded
bind:error
fadeIn
ratio="16x9"
src=""
on:load={(e) => {
console.log(e.detail); // null
}}
on:error={(e) => {
console.log(e.detail); // null
}}
/>

View file

@ -0,0 +1,64 @@
<svelte:options accessors />
<script lang="ts">
import { ImageLoader, InlineLoading } from "carbon-components-svelte";
// Valid image URL for testing successful loads
const validImageSrc =
"https://upload.wikimedia.org/wikipedia/commons/5/51/IBM_logo.svg";
// Invalid image URL for testing error states
const invalidImageSrc = "https://invalid-url/nonexistent.png";
export let imageLoader: ImageLoader;
</script>
<!-- Default image loader -->
<div data-testid="default-loader">
<ImageLoader src={validImageSrc} alt="IBM Logo" />
</div>
<!-- Image loader with loading and error slots -->
<div data-testid="loader-with-slots">
<ImageLoader src={validImageSrc} alt="IBM Logo with slots">
<svelte:fragment slot="loading">
<div data-testid="loading-state">
<InlineLoading />
</div>
</svelte:fragment>
<svelte:fragment slot="error">
<div data-testid="error-state">An error occurred.</div>
</svelte:fragment>
</ImageLoader>
</div>
<!-- Image loader with aspect ratio -->
<div data-testid="loader-with-ratio">
<ImageLoader
ratio="16x9"
src={validImageSrc}
alt="IBM Logo with aspect ratio"
/>
</div>
<!-- Image loader with fade in effect -->
<div data-testid="loader-with-fade">
<ImageLoader fadeIn src={validImageSrc} alt="IBM Logo with fade" />
</div>
<!-- Image loader with programmatic control -->
<div data-testid="programmatic-loader">
<ImageLoader
bind:this={imageLoader}
src=""
alt="Programmatically loaded image"
/>
</div>
<!-- Image loader that will trigger error state -->
<div data-testid="error-loader">
<ImageLoader src={invalidImageSrc} alt="Error state image">
<svelte:fragment slot="error">
<div data-testid="error-message">Failed to load image</div>
</svelte:fragment>
</ImageLoader>
</div>

View file

@ -0,0 +1,129 @@
import { render, screen } from "@testing-library/svelte";
import ImageLoader from "./ImageLoader.test.svelte";
describe("ImageLoader", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
it("renders with default props", () => {
render(ImageLoader);
const wrapper = screen.getByTestId("default-loader");
const img = wrapper.querySelector("img");
expect(img).toBeDefined();
});
it("shows loading state and transitions to loaded state", async () => {
render(ImageLoader);
const loadingIndicator = screen.getByTestId("loading-state");
expect(loadingIndicator).toBeInTheDocument();
const wrapper = screen.getByTestId("loader-with-slots");
const img = wrapper.querySelector("img");
expect(img).toBeDefined();
if (img) {
const loadEvent = new Event("load");
img.dispatchEvent(loadEvent);
expect(screen.queryByTestId("loading-state")).not.toBeInTheDocument();
expect(img).toBeVisible();
}
});
it("handles error state correctly", async () => {
render(ImageLoader);
const wrapper = screen.getByTestId("error-loader");
const img = wrapper.querySelector("img");
expect(img).toBeDefined();
if (img) {
const errorEvent = new Event("error");
img.dispatchEvent(errorEvent);
const errorMessage = screen.getByTestId("error-message");
expect(errorMessage).toBeInTheDocument();
expect(errorMessage).toHaveTextContent("Failed to load image");
}
});
it("supports aspect ratio", () => {
render(ImageLoader);
const wrapper = screen.getByTestId("loader-with-ratio");
const aspectRatioWrapper = wrapper.querySelector(
"[class*='bx--aspect-ratio']",
);
expect(aspectRatioWrapper).toHaveClass("bx--aspect-ratio--16x9");
});
it("supports fade in animation", async () => {
render(ImageLoader);
const wrapper = screen.getByTestId("loader-with-fade");
const img = wrapper.querySelector("img");
expect(img).toBeDefined();
if (img) {
const loadEvent = new Event("load");
img.dispatchEvent(loadEvent);
expect(img).toHaveStyle({
transition: expect.stringContaining("opacity"),
});
}
});
it("supports programmatic image loading", async () => {
const { component } = render(ImageLoader);
const wrapper = screen.getByTestId("programmatic-loader");
const img = wrapper.querySelector("img");
expect(img).toBeDefined();
const imageLoaderComponent = component.imageLoader;
expect(imageLoaderComponent).toBeDefined();
const newSrc = "https://example.com/new-image.jpg";
imageLoaderComponent.loadImage(newSrc);
if (img) {
expect(img.getAttribute("src")).toBe(newSrc);
}
});
it("dispatches load and error events", async () => {
const { component } = render(ImageLoader);
const load = vi.fn();
const error = vi.fn();
component.$on("load", load);
component.$on("error", error);
const defaultWrapper = screen.getByTestId("default-loader");
const defaultImg = defaultWrapper.querySelector("img");
expect(defaultImg).toBeDefined();
if (defaultImg) {
defaultImg.dispatchEvent(new Event("load"));
expect(load).toHaveBeenCalled();
}
const errorWrapper = screen.getByTestId("error-loader");
const errorImg = errorWrapper.querySelector("img");
expect(errorImg).toBeDefined();
if (errorImg) {
errorImg.dispatchEvent(new Event("error"));
expect(error).toHaveBeenCalled();
}
});
});