Skip to content

Commit

Permalink
More tests for image window loader
Browse files Browse the repository at this point in the history
  • Loading branch information
jassmith committed Oct 3, 2023
1 parent 2b2cff1 commit 610de9c
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 2 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/common/image-window-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ class ImageWindowLoaderImpl implements ImageWindowLoader {
private imageLoaded: (locations: readonly Item[]) => void = () => undefined;
private loadedLocations: [number, number][] = [];

private visibleWindow: Rectangle = {
public visibleWindow: Rectangle = {
x: 0,
y: 0,
width: 0,
height: 0,
};

private freezeCols: number = 0;
public freezeCols: number = 0;

private isInWindow = (packed: number) => {
const col = unpackCol(packed);
Expand Down
209 changes: 209 additions & 0 deletions packages/core/test/image-window-loader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import ImageWindowLoaderImpl from "../src/common/image-window-loader";

describe("ImageWindowLoaderImpl", () => {
let loader: ImageWindowLoaderImpl;

beforeEach(() => {
loader = new ImageWindowLoaderImpl();
});

describe("setWindow()", () => {
it("should set the new visible window and freezeCols", () => {
const newWindow = {
x: 10,
y: 10,
width: 100,
height: 100,
};
const freezeCols = 5;

loader.setWindow(newWindow, freezeCols);

// Assuming you modify your class to expose `visibleWindow` and `freezeCols` for testing
expect(loader.visibleWindow).toEqual(newWindow);
expect(loader.freezeCols).toBe(freezeCols);
});

it("should call clearOutOfWindow() if the window or freezeCols changes", () => {
const spyClearOutOfWindow = jest.spyOn(loader, "clearOutOfWindow" as any); // Private method, so using 'as any'

const window1 = {
x: 10,
y: 10,
width: 100,
height: 100,
};
const window2 = {
x: 15,
y: 15,
width: 100,
height: 100,
};
const freezeCols1 = 5;
const freezeCols2 = 10;

loader.setWindow(window1, freezeCols1);
expect(spyClearOutOfWindow).toHaveBeenCalledTimes(1);

loader.setWindow(window2, freezeCols1);
expect(spyClearOutOfWindow).toHaveBeenCalledTimes(2);

loader.setWindow(window2, freezeCols2);
expect(spyClearOutOfWindow).toHaveBeenCalledTimes(3);

// Cleanup
spyClearOutOfWindow.mockRestore();
});

it("should not call clearOutOfWindow() if the window and freezeCols stay the same", () => {
const spyClearOutOfWindow = jest.spyOn(loader, "clearOutOfWindow" as any);

const newWindow = {
x: 10,
y: 10,
width: 100,
height: 100,
};
const freezeCols = 5;

loader.setWindow(newWindow, freezeCols);
loader.setWindow(newWindow, freezeCols);

expect(spyClearOutOfWindow).toHaveBeenCalledTimes(1);

// Cleanup
spyClearOutOfWindow.mockRestore();
});
});

describe("loadOrGetImage()", () => {
// Define image data that might be used across tests
const url = "http://example.com/image.png";
const col = 1;
const row = 1;

it("should return the image if it is in the cache", () => {
// Setup: Mock the cache to contain the image
const img = new Image();
const key = url;
(loader as any).cache[key] = {
img,
url,
cells: [],
cancel: jest.fn(),
};

// Act: Retrieve the image
const result = loader.loadOrGetImage(url, col, row);

// Assert: Ensure the returned image is what we expect
expect(result).toBe(img);
});

it("should trigger image loading if the image is not in the cache", () => {
// Setup: Spy on `loadImage` method and clear the cache
const spyLoadImage = jest.spyOn(loader, "loadImage" as any);
(loader as any).cache = {};

// Act: Try to retrieve an image that's not in the cache
loader.loadOrGetImage(url, col, row);

// Assert: Ensure `loadImage` was called to fetch the image
expect(spyLoadImage).toHaveBeenCalledWith(url, col, row, url);

// Cleanup: Restore the spy
spyLoadImage.mockRestore();
});

it("should not trigger image loading if the image is in the cache but not yet loaded", () => {
// Setup: Mock the cache with a non-loaded image and spy on `loadImage` method
const key = url;
(loader as any).cache[key] = {
img: undefined,
url,
cells: [],
cancel: jest.fn(),
};
const spyLoadImage = jest.spyOn(loader, "loadImage" as any);

// Act: Try to retrieve an image that's in the cache but not loaded
const result = loader.loadOrGetImage(url, col, row);

// Assert: Ensure the image isn't returned and `loadImage` wasn't called
expect(result).toBeUndefined();
expect(spyLoadImage).not.toHaveBeenCalled();

// Cleanup: Restore the spy
spyLoadImage.mockRestore();
});
});

describe("loadImage()", () => {
// Define image data that might be used across tests
const url = "http://example.com/image.png";
const col = 1;
const row = 1;
const key = url;

it("should add a loading image to the cache", () => {
// Act: Load an image
(loader as any).loadImage(url, col, row, key);

// Assert: Ensure the cache was updated with an entry for the loading image
expect((loader as any).cache[key]).toBeDefined();
expect((loader as any).cache[key].url).toBe(url);
expect((loader as any).cache[key].img).toBeUndefined();
expect((loader as any).cache[key].cells).toEqual([row * (1 << 16) + col]);
});

it("should handle image loading and decoding successfully", async () => {
// Setup: Spy on `Image` constructor and `decode` method
const img = new Image();
img.addEventListener = jest.fn((_kind: string, cb: any) => cb());
jest.useFakeTimers();

const spyConstructor = jest.spyOn(window, "Image").mockImplementation(() => img);
img.decode = jest.fn().mockResolvedValue(undefined);

// Act: Load an image and simulate its successful loading
(loader as any).loadImage(url, col, row, key);

jest.runAllTimers();

jest.useRealTimers();
await new Promise(r => setTimeout(r, 100));

// Assert: Ensure `decode` was called and the image data is updated in the cache
expect(img.decode).toHaveBeenCalled();
expect((loader as any).cache[key].img).toBe(img);

// Cleanup: Restore the spies
spyConstructor.mockRestore();
});

it("should handle image loading failure gracefully", async () => {
// Setup: Spy on `Image` constructor and `decode` method
const img = new Image();
img.addEventListener = jest.fn((_kind: string, cb: any) => cb());
jest.useFakeTimers();

const spyConstructor = jest.spyOn(window, "Image").mockImplementation(() => img);
img.decode = jest.fn().mockRejectedValue(new Error("Decoding failed"));

// Act: Load an image and simulate its successful loading but decoding failure
(loader as any).loadImage(url, col, row, key);

jest.runAllTimers();

jest.useRealTimers();
await new Promise(r => setTimeout(r, 100));

// Assert: Ensure `decode` was called and the image data remains undefined in the cache
expect(img.decode).toHaveBeenCalled();
expect((loader as any).cache[key].img).toBeUndefined();

// Cleanup: Restore the spies
spyConstructor.mockRestore();
});
});
});

0 comments on commit 610de9c

Please sign in to comment.