From b410d7f0e96a982f9b5743224c5dcf4878dd30d2 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Fri, 6 May 2022 22:18:43 +0200 Subject: [PATCH] Feature/400/fetch images from url (#401) * (#400) Implement fetchFromUrl + tests * (#400) Re-export fetchFromUrl as part of the public API * (#400) Added docstring for fetchFromUrl --- index.ts | 1 + lib/imageResources.function.spec.ts | 45 ++++++++++++++++++++++++++++- lib/imageResources.function.ts | 36 +++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/index.ts b/index.ts index db8b6d43..e0336f23 100644 --- a/index.ts +++ b/index.ts @@ -52,6 +52,7 @@ const loadImage = providerRegistry.getImageReader().load; const saveImage = providerRegistry.getImageWriter().store; const imageResource = (fileName: string) => loadImageResource(providerRegistry, screen.config.resourceDirectory, fileName); +export {fetchFromUrl} from "./lib/imageResources.function"; export { clipboard, diff --git a/lib/imageResources.function.spec.ts b/lib/imageResources.function.spec.ts index a64600aa..9bd887f0 100644 --- a/lib/imageResources.function.spec.ts +++ b/lib/imageResources.function.spec.ts @@ -1,8 +1,9 @@ -import {loadImageResource} from "./imageResources.function"; +import {fetchFromUrl, loadImageResource} from "./imageResources.function"; import {mockPartial} from "sneer"; import {ProviderRegistry} from "./provider/provider-registry.class"; import {ImageReader} from "./provider"; import {join} from "path"; +import {ColorMode} from "./colormode.enum"; const loadMock = jest.fn(); const providerRegistryMock = mockPartial({ @@ -25,4 +26,46 @@ describe('imageResources', () => { // THEN expect(loadMock).toBeCalledWith(join(resourceDirectoryPath, imageFileName)); }); +}); + +describe('fetchFromUrl', () => { + it('should throw on malformed URLs', async () => { + // GIVEN + const malformedUrl = "foo"; + + // WHEN + const SUT = () => fetchFromUrl(malformedUrl); + + // THEN + await expect(SUT).rejects.toThrowError("Invalid URL"); + }); + + it('should throw on non-image URLs', async () => { + // GIVEN + const nonImageUrl = 'https://www.npmjs.com/package/jimp'; + + // WHEN + const SUT = () => fetchFromUrl(nonImageUrl); + + // THEN + await expect(SUT).rejects.toThrowError('Could not find MIME for Buffer'); + }); + + it('should return an RGB image from a valid URL', async () => { + // GIVEN + const validImageUrl = 'https://github.com/nut-tree/nut.js/raw/master/.gfx/nut.png'; + const expectedDimensions = { + width: 502, + height: 411 + }; + const expectedColorMode = ColorMode.RGB; + + // WHEN + const rgbImage = await fetchFromUrl(validImageUrl); + + // THEN + expect(rgbImage.colorMode).toBe(expectedColorMode); + expect(rgbImage.width).toBe(expectedDimensions.width); + expect(rgbImage.height).toBe(expectedDimensions.height); + }); }); \ No newline at end of file diff --git a/lib/imageResources.function.ts b/lib/imageResources.function.ts index 96ce588e..6613ec8b 100644 --- a/lib/imageResources.function.ts +++ b/lib/imageResources.function.ts @@ -1,7 +1,43 @@ import {join, normalize} from "path"; import {ProviderRegistry} from "./provider/provider-registry.class"; +import {URL} from "url"; +import {Image} from "./image.class"; +import Jimp from "jimp"; +import {ColorMode} from "./colormode.enum"; export function loadImageResource(providerRegistry: ProviderRegistry, resourceDirectory: string, fileName: string) { const fullPath = normalize(join(resourceDirectory, fileName)); return providerRegistry.getImageReader().load(fullPath); +} + +/** + * fetchFromUrl loads remote image content at runtime to provide it for further use in on-screen image search + * @param url The remote URl to fetch an image from as string or {@link URL} + * @throws On malformed URL input or in case of non-image remote content + */ +export async function fetchFromUrl(url: string | URL): Promise { + let imageUrl: URL; + if (url instanceof URL) { + imageUrl = url; + } else { + try { + imageUrl = new URL(url); + } catch (e) { + throw e; + } + } + return Jimp.read(imageUrl.href) + .then((image) => { + return new Image( + image.bitmap.width, + image.bitmap.height, + image.bitmap.data, + 4, + imageUrl.href, + ColorMode.RGB + ); + }) + .catch(err => { + throw err; + }); } \ No newline at end of file