waybetter-for-x/apps/desktop/tests/paneNav.test.ts
2026-10-01 06:01:13 +07:00

66 lines
3.3 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { isXUrl, redundantNavPanes, type PaneBox } from '../src/shell/paneModel.ts';
import { buildPaneNavCss, withAlpha } from '../src/inject/paneNav.ts';
const pane = (id: string, x: number, y = 0, extra: Partial<PaneBox> = {}): PaneBox =>
({ id, url: 'https://x.com/home', profile: 'default', x, y, visible: true, ...extra });
test('only the leftmost X pane of an account keeps the navigation', () => {
assert.deepEqual([...redundantNavPanes([pane('right', 800), pane('left', 0)])], ['right']);
assert.deepEqual([...redundantNavPanes([pane('a', 0), pane('b', 600), pane('c', 1200)])].sort(), ['b', 'c']);
});
test('stacked panes: the top one keeps it', () => {
assert.deepEqual([...redundantNavPanes([pane('bottom', 0, 400), pane('top', 0, 40)])], ['bottom']);
});
test('a single X pane, or one next to another site, keeps its navigation', () => {
assert.equal(redundantNavPanes([pane('only', 400)]).size, 0);
assert.equal(redundantNavPanes([pane('site', 0, 0, { url: 'https://github.com/' }), pane('x', 700)]).size, 0);
});
test('panes of different accounts each keep their own navigation', () => {
const panes = [pane('main', 0), pane('alt', 700, 0, { profile: 'p2' }), pane('main-2', 1400)];
assert.deepEqual([...redundantNavPanes(panes)], ['main-2']);
});
test('hidden tabs are not judged and do not take the lead', () => {
const panes = [pane('hidden-left', 0, 0, { visible: false }), pane('shown', 700)];
assert.equal(redundantNavPanes(panes).size, 0);
});
test('isXUrl: x.com and twitter.com over https only', () => {
for (const url of ['https://x.com/home', 'https://twitter.com/jack', 'https://mobile.x.com/i/chat']) assert.ok(isXUrl(url), url);
for (const url of ['http://x.com/home', 'https://x.com.evil.test/', 'https://notx.com/', 'about:blank', 'nonsense']) assert.equal(isXUrl(url), false, url);
});
const PAINT = { width: 88, background: 'rgb(0, 0, 0)', line: 'rgba(1, 2, 3, 0.16)', accent: 'rgb(29, 155, 240)', rtl: false };
test('pane CSS parks the rail only while the html attribute is set', () => {
const css = buildPaneNavCss(PAINT);
for (const rule of css.split('}').map(part => part.trim()).filter(Boolean)) {
const selector = rule.split('{')[0]!;
// Every rule is scoped: to the parked state, to our own edge element, or inside the motion query.
assert.ok(/html\[data-bt-pane-nav|#bt-rail-edge|@media/.test(selector), selector);
}
assert.match(css, /width: 88px !important/);
assert.match(css, /translateX\(-100%\)/);
assert.match(css, /\[data-bt-pane-nav="peek"\]/);
// Centering follows either kind of rail, not only X's classic header.
assert.match(css, /\[data-bt-rail\]\) ~ main\[role="main"\]/);
});
test('RTL: the rail parks and slides in on the right', () => {
const css = buildPaneNavCss({ ...PAINT, rtl: true });
assert.match(css, /right: 0 !important; left: auto !important/);
assert.match(css, /translateX\(100%\)/);
assert.doesNotMatch(css, /translateX\(-100%\)/);
assert.match(css, /#bt-rail-edge \{ position: fixed; right: 0/);
});
test('withAlpha keeps the channels and swaps the alpha', () => {
assert.equal(withAlpha('rgb(231, 233, 234)', 0.16), 'rgba(231, 233, 234, 0.16)');
assert.equal(withAlpha('rgba(15, 20, 25, 1)', 0.5), 'rgba(15, 20, 25, 0.5)');
assert.equal(withAlpha('', 0.2), 'rgba(128, 128, 128, 0.2)');
});