-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
338 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
import type { ApiLocale } from "@/lib/api/types"; | ||
import { | ||
addLocaleToPath, | ||
getDefaultLocaleFormLocales, | ||
isPathnameLocale, | ||
isPathnameMissingLocale, | ||
localizeHref, | ||
removeLocaleFromPath, | ||
} from "../lib/localization"; | ||
|
||
const LOCALES_MOCK = [ | ||
{ | ||
id: 2, | ||
name: "German (de)", | ||
code: "de", | ||
createdAt: "2023-12-15T20:24:52.563Z", | ||
updatedAt: "2023-12-15T20:24:52.563Z", | ||
isDefault: false, | ||
}, | ||
{ | ||
id: 1, | ||
name: "English (en)", | ||
code: "en", | ||
createdAt: "2023-12-15T20:24:19.257Z", | ||
updatedAt: "2023-12-15T20:24:19.257Z", | ||
isDefault: true, | ||
}, | ||
]; | ||
|
||
describe("localizeHref()", () => { | ||
describe("should localize when href is", () => { | ||
it("root-relative root", () => { | ||
const result = localizeHref("/", "en"); | ||
expect(result).toEqual("/en"); | ||
}); | ||
|
||
it("root-relative", () => { | ||
const result = localizeHref("/pathname", "en"); | ||
expect(result).toEqual("/en/pathname"); | ||
}); | ||
|
||
it("nested root-relative", () => { | ||
const result = localizeHref("/some/pathname", "en"); | ||
expect(result).toEqual("/en/some/pathname"); | ||
}); | ||
}); | ||
|
||
describe("should not localize href", () => { | ||
describe("when href is", () => { | ||
it("absolute", () => { | ||
const result = localizeHref("http://example.com/example", "en"); | ||
expect(result).toEqual("http://example.com/example"); | ||
}); | ||
|
||
it("relative", () => { | ||
const result = localizeHref("pathname", "en"); | ||
expect(result).toEqual("pathname"); | ||
}); | ||
|
||
it("anchor", () => { | ||
const result = localizeHref("#example", "en"); | ||
expect(result).toEqual("#example"); | ||
}); | ||
|
||
it("empty", () => { | ||
const result = localizeHref("", "en"); | ||
expect(result).toEqual(""); | ||
}); | ||
}); | ||
it("when locale is empty", () => { | ||
expect(localizeHref("/example", "")).toEqual("/example"); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("getDefaultLocaleFormLocales()", () => { | ||
it("returns the default locale when one is present", () => { | ||
const defaultLocale = getDefaultLocaleFormLocales(LOCALES_MOCK); | ||
expect(defaultLocale).toEqual(LOCALES_MOCK[1]); | ||
}); | ||
|
||
it("returns undefined when the locales list is empty", () => { | ||
const locales: ApiLocale[] = []; | ||
|
||
const defaultLocale = getDefaultLocaleFormLocales(locales); | ||
expect(defaultLocale).toBeUndefined(); | ||
}); | ||
|
||
it("returns undefined when there is no default locale", () => { | ||
const locales = LOCALES_MOCK.map((locale) => ({ | ||
...locale, | ||
isDefault: false, | ||
})); | ||
|
||
const defaultLocale = getDefaultLocaleFormLocales(locales); | ||
expect(defaultLocale).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
describe("removeLocaleFromPath()", () => { | ||
it("removes locale from start of the path", () => { | ||
const result = removeLocaleFromPath("/en/pathname", "en"); | ||
expect(result).toEqual("/pathname"); | ||
}); | ||
|
||
it("does not remove locale from middle of the path", () => { | ||
const result = removeLocaleFromPath("/pathname/en/abc", "en"); | ||
expect(result).toEqual("/pathname/en/abc"); | ||
}); | ||
|
||
it("returns '/' when path is just the locale", () => { | ||
const result = removeLocaleFromPath("/en", "en"); | ||
expect(result).toEqual("/"); | ||
}); | ||
|
||
it("returns original path when locale is not in the path", () => { | ||
const result = removeLocaleFromPath("/pathname/abc", "en"); | ||
expect(result).toEqual("/pathname/abc"); | ||
}); | ||
|
||
it("returns original path when path is locale-like string", () => { | ||
const result = removeLocaleFromPath("/enterprise", "en"); | ||
expect(result).toEqual("/enterprise"); | ||
}); | ||
}); | ||
|
||
describe("addLocaleToPath()", () => { | ||
describe("should add locale when", () => { | ||
it("path is root", () => { | ||
const result = addLocaleToPath("/", "en"); | ||
expect(result).toEqual("/en"); | ||
}); | ||
|
||
it("path is empty", () => { | ||
const result = addLocaleToPath("", "en"); | ||
expect(result).toEqual("/en"); | ||
}); | ||
|
||
it("path is '/pathname'", () => { | ||
const result = addLocaleToPath("/pathname", "en"); | ||
expect(result).toEqual("/en/pathname"); | ||
}); | ||
|
||
it("path is 'pathname'", () => { | ||
const result = addLocaleToPath("pathname", "en"); | ||
expect(result).toEqual("/en/pathname"); | ||
}); | ||
|
||
it("path is nested", () => { | ||
const result = addLocaleToPath("/some/pathname", "en"); | ||
expect(result).toEqual("/en/some/pathname"); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("isPathnameMissingLocale()", () => { | ||
describe("should return true when", () => { | ||
it("pathname is missing locale", () => { | ||
const result = isPathnameMissingLocale("/pathname", LOCALES_MOCK); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
it("pathname starts with locale-like string", () => { | ||
const result = isPathnameMissingLocale("/enterprise", LOCALES_MOCK); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
it("locales list is empty", () => { | ||
const result = isPathnameMissingLocale("/en/pathname", []); | ||
expect(result).toBe(true); | ||
}); | ||
}); | ||
|
||
describe("should return false when", () => { | ||
it("pathname starts with locale", () => { | ||
const result = isPathnameMissingLocale("/en/pathname", LOCALES_MOCK); | ||
expect(result).toBe(false); | ||
}); | ||
|
||
it("pathname is exactly the locale", () => { | ||
const result = isPathnameMissingLocale("/en", LOCALES_MOCK); | ||
expect(result).toBe(false); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("isPathnameLocale()", () => { | ||
describe("should return true", () => { | ||
it("when pathname starts with given locale", () => { | ||
const result = isPathnameLocale("/en/pathname", "en"); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
it("when pathname is exactly the given locale", () => { | ||
const result = isPathnameLocale("/en", "en"); | ||
expect(result).toBe(true); | ||
}); | ||
}); | ||
|
||
describe("should return false", () => { | ||
it("when pathname does not start with given locale", () => { | ||
const result = isPathnameLocale("/pathname/en", "en"); | ||
expect(result).toBe(false); | ||
}); | ||
|
||
it("when pathname starts with locale-like string of given locale", () => { | ||
const result = isPathnameLocale("/enterprise", "en"); | ||
expect(result).toBe(false); | ||
}); | ||
|
||
it("when given locale is undefined", () => { | ||
const result = isPathnameLocale("/en/pathname", undefined); | ||
expect(result).toBe(false); | ||
}); | ||
|
||
it("when pathname is not the given locale", () => { | ||
const result = isPathnameLocale("/de", "en"); | ||
expect(result).toBe(false); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/** | ||
* @jest-environment node | ||
*/ | ||
|
||
import { NextRequest, NextResponse } from "next/server"; | ||
import { fetchLocales } from "../lib/api"; | ||
import { middleware } from "../middleware"; | ||
|
||
jest.mock("../lib/api"); | ||
|
||
const LOCALES_MOCK = [ | ||
{ | ||
id: 2, | ||
name: "German (de)", | ||
code: "de", | ||
createdAt: "2023-12-15T20:24:52.563Z", | ||
updatedAt: "2023-12-15T20:24:52.563Z", | ||
isDefault: false, | ||
}, | ||
{ | ||
id: 1, | ||
name: "English (en)", | ||
code: "en", | ||
createdAt: "2023-12-15T20:24:19.257Z", | ||
updatedAt: "2023-12-15T20:24:19.257Z", | ||
isDefault: true, | ||
}, | ||
]; | ||
|
||
describe("middleware", () => { | ||
const rewriteSpy = jest.spyOn(NextResponse, "rewrite"); | ||
const redirectSpy = jest.spyOn(NextResponse, "redirect"); | ||
const DOMAIN_MOCK = "https://example.com"; | ||
|
||
beforeEach(() => { | ||
(fetchLocales as jest.Mock).mockResolvedValue(LOCALES_MOCK); | ||
}); | ||
|
||
afterEach(() => { | ||
rewriteSpy.mockClear(); | ||
redirectSpy.mockClear(); | ||
}); | ||
|
||
describe("should remove default locale from URL", () => { | ||
it("when pathname starts with default locale", async () => { | ||
const request = new NextRequest( | ||
new Request(`${DOMAIN_MOCK}/en/pathname`), | ||
); | ||
const response = await middleware(request); | ||
|
||
expect(redirectSpy).toHaveBeenCalledTimes(1); | ||
expect(rewriteSpy).toHaveBeenCalledTimes(0); | ||
expect(response).toBeInstanceOf(NextResponse); | ||
expect(response?.status).toEqual(307); | ||
expect(response?.headers.get("Location")).toEqual( | ||
`${DOMAIN_MOCK}/pathname`, | ||
); | ||
}); | ||
|
||
it("when pathname is exactly default locale", async () => { | ||
const request = new NextRequest(new Request(`${DOMAIN_MOCK}/en`)); | ||
const response = await middleware(request); | ||
|
||
expect(redirectSpy).toHaveBeenCalledTimes(1); | ||
expect(rewriteSpy).toHaveBeenCalledTimes(0); | ||
expect(response).toBeInstanceOf(NextResponse); | ||
expect(response?.status).toEqual(307); | ||
expect(response?.headers.get("Location")).toEqual(`${DOMAIN_MOCK}/`); | ||
}); | ||
}); | ||
|
||
describe("should not modify URL", () => { | ||
it("when pathname starts with non-default locale", async () => { | ||
const request = new NextRequest( | ||
new Request(`${DOMAIN_MOCK}/de/pathname`), | ||
); | ||
const response = await middleware(request); | ||
|
||
expect(redirectSpy).toHaveBeenCalledTimes(0); | ||
expect(rewriteSpy).toHaveBeenCalledTimes(0); | ||
expect(response).toBeUndefined(); | ||
}); | ||
|
||
it("when pathname is exactly non-default locale", async () => { | ||
const request = new NextRequest(new Request(`${DOMAIN_MOCK}/de`)); | ||
const response = await middleware(request); | ||
|
||
expect(redirectSpy).toHaveBeenCalledTimes(0); | ||
expect(rewriteSpy).toHaveBeenCalledTimes(0); | ||
expect(response).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
describe("should rewrite to default locale", () => { | ||
it("when pathname is missing locale", async () => { | ||
const request = new NextRequest(new Request(`${DOMAIN_MOCK}/pathname`)); | ||
const response = await middleware(request); | ||
|
||
expect(redirectSpy).toHaveBeenCalledTimes(0); | ||
expect(rewriteSpy).toHaveBeenCalledTimes(1); | ||
expect(response).toBeInstanceOf(NextResponse); | ||
expect(response?.status).toEqual(200); | ||
expect(response?.headers.get("Location")).toEqual(null); | ||
}); | ||
|
||
it("when pathname is '/'", async () => { | ||
const request = new NextRequest(new Request(`${DOMAIN_MOCK}/`)); | ||
const response = await middleware(request); | ||
|
||
expect(redirectSpy).toHaveBeenCalledTimes(0); | ||
expect(rewriteSpy).toHaveBeenCalledTimes(1); | ||
expect(response).toBeInstanceOf(NextResponse); | ||
expect(response?.status).toEqual(200); | ||
expect(response?.headers.get("Location")).toEqual(null); | ||
}); | ||
}); | ||
}); |