feat: switch avatar imager to epicnabbo.nl with figure conversion
- Update imager to use epicnabbo.nl by default with effect=14 and img_format=apng - Add figure string converter (old Habbo format -> epicnabbo.nl short format) - Update avatarImageUrl to auto-convert figure strings - Update online-users-widget to use new avatarImageUrl - Add tests for imager and figure conversion
This commit is contained in:
1 parent
703c29bc83
commit
fc7e6ca248
10 files changed
+270
-114
No files matched your search
+35
-57
@@ -2,69 +2,34 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const envMock = vi.hoisted(() => ({
|
||||
env: {
|
||||
NEXT_PUBLIC_IMAGER_URL: undefined as string | undefined,
|
||||
NEXT_PUBLIC_IMAGER_URL: "https://img.example.com" as string | undefined,
|
||||
NEXT_PUBLIC_APP_URL: undefined as string | undefined,
|
||||
IMAGING_UPSTREAM_URL: undefined as string | undefined,
|
||||
IMAGING_UPSTREAM_URL: "https://up.example.com" as string | undefined,
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/env", () => envMock);
|
||||
|
||||
import { getAvatarUrl, resolveImagerBase, resolveUpstreamBase } from "./imager";
|
||||
import { getAvatarUrl, resolveImagerBase } from "./imager";
|
||||
|
||||
describe("resolveImagerBase", () => {
|
||||
beforeEach(() => {
|
||||
envMock.env.NEXT_PUBLIC_IMAGER_URL = undefined;
|
||||
envMock.env.NEXT_PUBLIC_APP_URL = undefined;
|
||||
envMock.env.NEXT_PUBLIC_IMAGER_URL = "https://img.example.com";
|
||||
});
|
||||
|
||||
it("prefers a configured imager env var", () => {
|
||||
it("returns configured imager env var", () => {
|
||||
expect(resolveImagerBase()).toBe("https://img.example.com");
|
||||
});
|
||||
|
||||
it("strips trailing slashes", () => {
|
||||
envMock.env.NEXT_PUBLIC_IMAGER_URL = "https://img.example.com/";
|
||||
expect(resolveImagerBase()).toBe("https://img.example.com");
|
||||
});
|
||||
|
||||
it("ignores the broken self-hosted proxy env route", () => {
|
||||
envMock.env.NEXT_PUBLIC_IMAGER_URL =
|
||||
"https://img.example.com/api/imaging/avatar";
|
||||
envMock.env.NEXT_PUBLIC_APP_URL = "https://app.example.com/";
|
||||
expect(resolveImagerBase()).toBe("https://app.example.com/imaging");
|
||||
});
|
||||
|
||||
it("falls back to app URL imaging route", () => {
|
||||
envMock.env.NEXT_PUBLIC_APP_URL = "https://app.example.com/";
|
||||
expect(resolveImagerBase()).toBe("https://app.example.com/imaging");
|
||||
});
|
||||
|
||||
it("falls back to relative /imaging", () => {
|
||||
expect(resolveImagerBase()).toBe("/imaging");
|
||||
});
|
||||
|
||||
it("uses configured setting when valid", () => {
|
||||
expect(resolveImagerBase("https://cdn.example.com/imager/")).toBe(
|
||||
"https://cdn.example.com/imager",
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects configured setting that is the broken proxy route", () => {
|
||||
expect(
|
||||
resolveImagerBase("https://cdn.example.com/api/imaging/avatar"),
|
||||
).toBe("/imaging");
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveUpstreamBase", () => {
|
||||
beforeEach(() => {
|
||||
envMock.env.IMAGING_UPSTREAM_URL = undefined;
|
||||
});
|
||||
|
||||
it("uses env upstream when configured", () => {
|
||||
envMock.env.IMAGING_UPSTREAM_URL = "https://up.example.com/";
|
||||
expect(resolveUpstreamBase()).toBe("https://up.example.com");
|
||||
});
|
||||
|
||||
it("falls back to Habbo public imager", () => {
|
||||
expect(resolveUpstreamBase()).toBe(
|
||||
"https://www.habbo.com/habbo-imaging/avatarimage",
|
||||
it("falls back to epicnabbo.nl when env var is not set", () => {
|
||||
envMock.env.NEXT_PUBLIC_IMAGER_URL = undefined;
|
||||
expect(resolveImagerBase()).toBe(
|
||||
"https://epicnabbo.nl/imaging/avatarimage",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -72,7 +37,6 @@ describe("resolveUpstreamBase", () => {
|
||||
describe("getAvatarUrl", () => {
|
||||
beforeEach(() => {
|
||||
envMock.env.NEXT_PUBLIC_IMAGER_URL = "https://img.example.com";
|
||||
envMock.env.NEXT_PUBLIC_APP_URL = undefined;
|
||||
});
|
||||
|
||||
it("builds a query string with defaults", () => {
|
||||
@@ -82,7 +46,8 @@ describe("getAvatarUrl", () => {
|
||||
expect(url).toContain("direction=2");
|
||||
expect(url).toContain("head_direction=3");
|
||||
expect(url).toContain("size=m");
|
||||
expect(url).toContain("img_format=png");
|
||||
expect(url).toContain("img_format=apng");
|
||||
expect(url).toContain("effect=14");
|
||||
});
|
||||
|
||||
it("honors custom options", () => {
|
||||
@@ -102,15 +67,28 @@ describe("getAvatarUrl", () => {
|
||||
expect(url).toContain("action=sit");
|
||||
});
|
||||
|
||||
it("uses app URL imaging route when imager env absent", () => {
|
||||
envMock.env.NEXT_PUBLIC_IMAGER_URL = undefined;
|
||||
envMock.env.NEXT_PUBLIC_APP_URL = "https://app.example.com/";
|
||||
const url = getAvatarUrl("hd-180-1");
|
||||
expect(url.startsWith("https://app.example.com/imaging?")).toBe(true);
|
||||
});
|
||||
|
||||
it("escapes figure values in the query string", () => {
|
||||
const url = getAvatarUrl("hd-180-1&x=1");
|
||||
expect(url).not.toContain("&x=1");
|
||||
});
|
||||
|
||||
it("allows custom img_format", () => {
|
||||
const url = getAvatarUrl("hd-180-1", { img_format: "png" });
|
||||
expect(url).toContain("img_format=png");
|
||||
});
|
||||
|
||||
it("allows custom effect", () => {
|
||||
const url = getAvatarUrl("hd-180-1", { effect: 0 });
|
||||
expect(url).toContain("effect=0");
|
||||
});
|
||||
|
||||
it("uses epicnabbo.nl when no env var is set", () => {
|
||||
envMock.env.NEXT_PUBLIC_IMAGER_URL = undefined;
|
||||
const url = getAvatarUrl("hd-180-1");
|
||||
expect(url.startsWith("https://epicnabbo.nl/imaging/avatarimage?")).toBe(
|
||||
true,
|
||||
);
|
||||
expect(url).toContain("img_format=apng");
|
||||
expect(url).toContain("effect=14");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user