alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
108 lines (94 loc) • 3.58 kB
text/typescript
import { Alepha } from "alepha";
import {
$page,
AlephaReactRouter,
ReactPageProvider,
RouterLocaleProvider,
} from "alepha/react/router";
import { ServerRouterProvider } from "alepha/server";
import { describe, test } from "vitest";
import { AlephaReactI18n } from "../index.ts";
import { $dictionary } from "../primitives/$dictionary.ts";
import { I18nProvider } from "../providers/I18nProvider.ts";
/**
* End-to-end wiring of locale-prefix routing (`routing: "prefix"`) across the
* i18n and router modules: configuration, outbound URL prefixing, and the
* URL-as-source-of-truth language resolution.
*/
describe("locale-prefix routing", () => {
class App {
en = $dictionary({
lazy: async () => ({ default: { hi: "Hi" } }),
});
fr = $dictionary({
lazy: async () => ({ default: { hi: "Salut" } }),
});
about = $page({
name: "about",
path: "/about",
component: () => "about",
});
}
// Everything is injected BEFORE start() — the DI container locks once started.
const start = async (routing: "none" | "prefix") => {
const alepha = Alepha.create()
.with(AlephaReactRouter)
.with(AlephaReactI18n);
alepha.inject(App);
const i18n = alepha.inject(I18nProvider);
const locale = alepha.inject(RouterLocaleProvider);
const pages = alepha.inject(ReactPageProvider);
const server = alepha.inject(ServerRouterProvider);
i18n.options.routing = routing;
await alepha.start();
return { alepha, i18n: i18n as any, locale, pages, server };
};
test("i18n configures the router locale provider from its dictionaries", async ({
expect,
}) => {
const { locale } = await start("prefix");
expect(locale.enabled).toBe(true);
expect(locale.defaultLocale).toBe("en");
expect(locale.locales).toEqual(["en", "fr"]);
expect(locale.prefixedLocales).toEqual(["fr"]);
});
test("generated URLs carry the active locale prefix (default stays bare)", async ({
expect,
}) => {
const { pages, locale } = await start("prefix");
// default locale → unprefixed
expect(pages.pathname("about")).toBe("/about");
// active locale fr → prefixed
locale.current = "fr";
expect(pages.pathname("about")).toBe("/fr/about");
});
test("the URL locale wins over cookie and Accept-Language", async ({
expect,
}) => {
const { i18n } = await start("prefix");
// url prefix beats a conflicting cookie + header
expect(i18n.resolveRequestLang("en", "en", "fr")).toBe("fr");
// an unprefixed path resolves to the default locale (no redirect)
expect(i18n.detectUrlLocale("/about")).toBe("en");
expect(i18n.detectUrlLocale("/fr/about")).toBe("fr");
});
test("registers prefixed server route variants for non-default locales", async ({
expect,
}) => {
const { server } = await start("prefix");
const paths = server.getRoutes().map((route) => route.path);
expect(paths).toContain("/about");
expect(paths).toContain("/fr/about");
// the default locale is never prefixed
expect(paths).not.toContain("/en/about");
});
test("stays inert when routing is left as 'none'", async ({ expect }) => {
const { i18n, pages, locale, server } = await start("none");
expect(locale.enabled).toBe(false);
expect(pages.pathname("about")).toBe("/about");
expect(i18n.detectUrlLocale("/fr/about")).toBeUndefined();
const paths = server.getRoutes().map((route) => route.path);
expect(paths).toContain("/about");
expect(paths).not.toContain("/fr/about");
});
});