Skip to content
Merged
Show file tree
Hide file tree
Changes from 106 commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
2179a85
feat(fe): name the browser a session was created from
sea-snake Aug 22, 2026
e0b729f
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Aug 25, 2026
dbca3fb
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Aug 25, 2026
6c475b9
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Aug 25, 2026
5e44017
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Aug 31, 2026
83d5862
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Aug 31, 2026
b57732a
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Aug 31, 2026
807ae42
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 1, 2026
861a87f
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 1, 2026
5073b35
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 1, 2026
49f9472
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 1, 2026
4a13613
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 1, 2026
b1b9df8
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 2, 2026
681b554
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 4, 2026
43b85ef
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 4, 2026
ffcc72b
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 4, 2026
2ffcce9
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 4, 2026
6815d15
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 4, 2026
02fa4fd
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 4, 2026
b2fab42
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 4, 2026
d3d3cf1
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
b310dce
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
5070cb3
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
7c665f3
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
c34b091
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
a1f822e
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
afed366
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
483c552
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
024ee7c
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
0702e23
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
dfa15e1
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
3722872
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
e6bb005
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
152ab89
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
84d1d2f
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
91e085a
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
9a9a548
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
da4998c
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
e19fb37
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
a7f578f
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
3cdca40
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
7cca3fd
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
a419ada
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
e77ad43
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
d5c9535
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
6afa078
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
935aee7
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
7f110ca
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
67d8a49
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 5, 2026
ddcd375
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
d75c064
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
18bcdd7
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
a4bd52c
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
a9fa3d1
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
5a2f098
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
2825613
refactor(be): a browser, not a session device
sea-snake Sep 6, 2026
0784a43
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
0515304
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
30ffa14
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
a5531d6
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
5648ab5
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
17e4cf5
refactor(fe): the name-length bound says browser
sea-snake Sep 6, 2026
b101696
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
2dc03f8
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
d8f9690
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
56ff752
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
5d64ba4
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 6, 2026
8857792
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
99e5288
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
3edb915
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
7bef55c
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
bb12de2
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
e4dad50
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
bde8bc5
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
0633973
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
6be5b59
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
2da5f64
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
4ea8177
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
25c61ff
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 7, 2026
6e8c3f5
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 8, 2026
6b57e5b
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 8, 2026
e8622b3
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 8, 2026
04c01ff
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 8, 2026
e048ead
chore: merge fe/authorization-request-queue
sea-snake Sep 8, 2026
5044634
chore: merge fe/authorization-request-queue
sea-snake Sep 8, 2026
f71e0ca
chore: merge fe/authorization-request-queue
sea-snake Sep 8, 2026
3755e7f
chore: merge fe/authorization-request-queue
sea-snake Sep 8, 2026
262832c
chore: merge fe/authorization-request-queue
sea-snake Sep 8, 2026
5cf7db1
chore: merge fe/authorization-request-queue
sea-snake Sep 8, 2026
8478942
feat(frontend): describe the browser in tokens instead of composing a…
sea-snake Sep 8, 2026
52af608
chore: merge fe/authorization-request-queue
sea-snake Sep 8, 2026
786f1ba
chore: merge fe/authorization-request-queue
sea-snake Sep 9, 2026
6213822
feat(frontend): resolve the six named brands, and name the rest
sea-snake Sep 9, 2026
ab54c58
chore: merge fe/authorization-request-queue
sea-snake Sep 9, 2026
5680c83
fix(fe): handle the absent form-factor hint explicitly
sea-snake Sep 9, 2026
0dc6938
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 9, 2026
9b29e4c
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 9, 2026
6dc3b56
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 9, 2026
c496493
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 9, 2026
b547365
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 9, 2026
fe02515
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 9, 2026
8b42ad9
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 9, 2026
022f107
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 10, 2026
d192e97
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 10, 2026
0ffa028
refactor(browser): name a browser by the token it gives, wherever it …
sea-snake Sep 10, 2026
b6a514e
Merge branch 'fe/authorization-request-queue' into fe/describe-browser
sea-snake Sep 10, 2026
c46153f
fix(describe-browser): name only the form factors the canister has
sea-snake Sep 10, 2026
c83b3e0
style(describe-browser): format the test file
sea-snake Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
331 changes: 331 additions & 0 deletions src/frontend/src/lib/utils/describeBrowser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
import { afterEach, describe, expect, it } from "vitest";
import { describeBrowser } from "./describeBrowser";
import type {
BrowserBrand,
BrowserDescription,
FormFactor,
OperatingSystem,
} from "$lib/generated/internet_identity_types";

const CHROME_ANDROID =
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36";

const stub = (props: Record<string, unknown>): void => {
for (const [name, value] of Object.entries(props)) {
Object.defineProperty(navigator, name, { value, configurable: true });
}
};

const describing = (
agent: string,
maxTouchPoints = 0,
): Promise<BrowserDescription> => {
stub({ userAgent: agent, maxTouchPoints });
return describeBrowser();
};

afterEach(() => {
stub({ userAgentData: undefined });
});

/**
* Real agent strings throughout: what makes these come out right is how the tokens sit
* relative to one another, which a hand-made string would not reproduce.
*/
describe("brand", () => {
/// The six this interface names. Each is read from a token more specific than the
/// `Chrome/` and `Safari/` every one of them also carries.
it.each([
{
name: "Chrome on iOS, which says CriOS",
agent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1",
brand: { Chrome: null } satisfies BrowserBrand,
},
{
name: "Firefox on iOS, which says FxiOS",
agent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/126.1 Mobile/15E148 Safari/605.1.15",
brand: { Firefox: null } satisfies BrowserBrand,
},
{
name: "Edge on iOS, which says EdgiOS",
agent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 EdgiOS/125.2535.60 Mobile/15E148 Safari/605.1.15",
brand: { Edge: null } satisfies BrowserBrand,
},
{
name: "Opera on iOS, which says OPT",
agent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) OPT/4.4.0 Mobile/15E148 Safari/604.1",
brand: { Opera: null } satisfies BrowserBrand,
},
{
name: "Safari, whose own token is the WebKit build",
agent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1",
brand: { Safari: null } satisfies BrowserBrand,
},
{
name: "Firefox on a Mac",
agent:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:126.0) Gecko/20100101 Firefox/126.0",
brand: { Firefox: null } satisfies BrowserBrand,
},
{
name: "Edge on Windows",
agent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0",
brand: { Edge: null } satisfies BrowserBrand,
},
{
name: "Opera on Windows",
agent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 OPR/110.0.0.0",
brand: { Opera: null } satisfies BrowserBrand,
},
{
name: "Samsung Internet",
agent:
"Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36",
brand: { SamsungInternet: null } satisfies BrowserBrand,
},
{
name: "plain Chrome, whose last token is Safari",
agent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
brand: { Chrome: null } satisfies BrowserBrand,
},
])("names $name", async ({ agent, brand }) => {
await expect(describing(agent)).resolves.toMatchObject({ brand });
});

/// A browser outside the six is named by the token it appends, not by the agent it
/// borrowed — otherwise every Chromium fork reads as Chrome. Read from the agent
/// rather than listed, so one this frontend has never heard of still arrives named.
it.each([
{
name: "Vivaldi",
agent:
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Vivaldi/6.7.3329.41",
other: "Vivaldi",
},
{
name: "Yandex, which this frontend has never been taught",
agent:
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 YaBrowser/24.4.1 Safari/537.36",
other: "YaBrowser",
},
])("names $name by its own token", async ({ agent, other }) => {
await expect(describing(agent)).resolves.toMatchObject({
brand: { Other: other },
});
});

/// Brave ships Chrome's agent byte for byte, deliberately. There is no token to find
/// and nothing to report but Chrome — which is the honest answer, not a gap.
it("reads a browser that hides itself as the one it imitates", async () => {
await expect(
describing(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
),
).resolves.toMatchObject({ brand: { Chrome: null } });
});

/// The canister fixes a description at registration, so a version captured here would
/// sit frozen at whichever build first signed in.
it("keeps the version out of the name", async () => {
const { brand } = await describing(
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Vivaldi/6.7.3329.41",
);

expect(brand).toEqual({ Other: "Vivaldi" });
});
});

describe("operating system", () => {
it.each([
{
name: "Chromebook",
agent:
"Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
os: { ChromeOs: null } satisfies OperatingSystem,
},
{
name: "Android",
agent: CHROME_ANDROID,
os: { Android: null } satisfies OperatingSystem,
},
{
name: "iPhone",
agent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1",
os: { Ios: null } satisfies OperatingSystem,
},
{
name: "iPad",
agent:
"Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1",
os: { Ipados: null } satisfies OperatingSystem,
},
{
name: "Windows",
agent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
os: { Windows: null } satisfies OperatingSystem,
},
{
name: "Linux",
agent:
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
os: { Linux: null } satisfies OperatingSystem,
},
])("reads $name", async ({ agent, os }) => {
await expect(describing(agent)).resolves.toMatchObject({ os });
});

/// An iPad in desktop mode sends a Mac agent and exposes no hints, so the touch points
/// are the only thing telling it from a Mac. A Mac reports none.
it.each([
{ name: "a Mac", touchPoints: 0, os: { Macos: null } },
{
name: "an iPad pretending to be one",
touchPoints: 5,
os: { Ipados: null },
},
])("tells $name apart by its touch points", async ({ touchPoints, os }) => {
await expect(
describing(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
touchPoints,
),
).resolves.toMatchObject({ os });
});

/// A platform none of the seven names is still named by the agent, in the first
/// segment of its parenthesised block — the whole agent is not an operating system.
it("takes an unknown platform from where the agent states it", async () => {
await expect(
describing(
"Mozilla/5.0 (Haiku; U; Haiku BePC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
),
).resolves.toMatchObject({ os: { Other: "Haiku" } });
});
});

describe("form factor", () => {
it.each([
{
name: "a phone from its agent",
agent: CHROME_ANDROID,
touchPoints: 5,
form_factor: { Mobile: null } satisfies FormFactor,
},
{
name: "an Android tablet, which says Android without saying Mobile",
agent:
"Mozilla/5.0 (Linux; Android 13; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
touchPoints: 5,
form_factor: { Tablet: null } satisfies FormFactor,
},
{
name: "a desktop",
agent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
touchPoints: 0,
form_factor: { Desktop: null } satisfies FormFactor,
},
])("reads $name", async ({ agent, touchPoints, form_factor }) => {
await expect(describing(agent, touchPoints)).resolves.toMatchObject({
form_factor,
});
});
});

describe("client hints", () => {
const withHints = (high: unknown, mobile = true) => {
stub({
userAgent: CHROME_ANDROID,
maxTouchPoints: 5,
userAgentData: {
mobile,
getHighEntropyValues: () => Promise.resolve(high),
},
});
return describeBrowser();
};

it("takes the model the platform reports", async () => {
await expect(withHints({ model: "Pixel 5" })).resolves.toMatchObject({
model: ["Pixel 5"],
});
});

/// Android reports the field with nothing in it off a phone, and an absent model has
/// to stay absent rather than becoming an empty string in the record.
it("treats an empty model as no model", async () => {
await expect(withHints({ model: "" })).resolves.toMatchObject({
model: [],
});
});

/// The current spec says `formFactors`; the versions that shipped it first said
/// `formFactor`. A tablet has to read as one on both.
it.each([
{
name: "the plural the spec settled on",
high: { formFactors: ["Tablet"] },
},
{ name: "the singular that shipped first", high: { formFactor: "Tablet" } },
])("takes a stated form factor in $name", async ({ high }) => {
await expect(withHints(high)).resolves.toMatchObject({
form_factor: { Tablet: null },
});
});

it("still describes the browser when the platform refuses the question", async () => {
stub({
userAgent: CHROME_ANDROID,
maxTouchPoints: 5,
userAgentData: {
mobile: true,
getHighEntropyValues: () => Promise.reject(new Error("not allowed")),
},
});

await expect(describeBrowser()).resolves.toEqual({
brand: { Chrome: null },
os: { Android: null },
form_factor: { Mobile: null },
model: [],
});
});
});

/// The canister refuses a token over its cap, so a resolver never offers one.
describe("token limits", () => {
it("caps a brand it read off the agent", async () => {
const { brand } = await describing(
`Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ${"B".repeat(200)}/1.0`,
);

const token = "Other" in brand ? brand.Other : "";
expect(new TextEncoder().encode(token).length).toBeLessThanOrEqual(64);
});

it("caps a model the platform reported", async () => {
stub({
userAgent: CHROME_ANDROID,
maxTouchPoints: 5,
userAgentData: {
mobile: true,
getHighEntropyValues: () => Promise.resolve({ model: "M".repeat(200) }),
},
});

const { model } = await describeBrowser();
expect(new TextEncoder().encode(model[0] ?? "").length).toBeLessThanOrEqual(
64,
);
});
});
Loading
Loading