mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-08-08 09:43:03 +02:00
Add hop width bit display display. Closes #323.
This commit is contained in:
@@ -4,6 +4,7 @@ import { useState } from 'react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { MessageList } from '../components/MessageList';
|
||||
import { PathHopWidthProvider } from '../contexts/PathHopWidthContext';
|
||||
import { CONTACT_TYPE_ROOM, type Contact, type Message } from '../types';
|
||||
|
||||
const scrollIntoViewMock = vi.fn();
|
||||
@@ -87,6 +88,66 @@ describe('MessageList channel sender rendering', () => {
|
||||
expect(screen.queryByText('nl-gr')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows per-hop byte width in the path badge when the toggle is on', () => {
|
||||
render(
|
||||
<PathHopWidthProvider showPathHopWidth setShowPathHopWidth={() => {}}>
|
||||
<MessageList
|
||||
messages={[
|
||||
createMessage({
|
||||
sender_name: 'Alice',
|
||||
// 8 hex chars over 2 hops = 2 bytes/hop.
|
||||
paths: [{ path: 'AABBCCDD', path_len: 2, received_at: 1700000001 }],
|
||||
}),
|
||||
]}
|
||||
contacts={[]}
|
||||
loading={false}
|
||||
/>
|
||||
</PathHopWidthProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('(2 · 2B)')).toBeInTheDocument();
|
||||
expect(screen.getByTitle('View message path (2B per hop)')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides the width by default (toggle off) and shows only the hop count', () => {
|
||||
render(
|
||||
<MessageList
|
||||
messages={[
|
||||
createMessage({
|
||||
sender_name: 'Alice',
|
||||
paths: [{ path: 'AABBCCDD', path_len: 2, received_at: 1700000001 }],
|
||||
}),
|
||||
]}
|
||||
contacts={[]}
|
||||
loading={false}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('(2)')).toBeInTheDocument();
|
||||
expect(screen.queryByText('(2 · 2B)')).not.toBeInTheDocument();
|
||||
expect(screen.getByTitle('View message path')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('omits the width for direct (0-hop) paths even when the toggle is on', () => {
|
||||
render(
|
||||
<PathHopWidthProvider showPathHopWidth setShowPathHopWidth={() => {}}>
|
||||
<MessageList
|
||||
messages={[
|
||||
createMessage({
|
||||
sender_name: 'Alice',
|
||||
paths: [{ path: '', path_len: 0, received_at: 1700000001 }],
|
||||
}),
|
||||
]}
|
||||
contacts={[]}
|
||||
loading={false}
|
||||
/>
|
||||
</PathHopWidthProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('(d)')).toBeInTheDocument();
|
||||
expect(screen.getByTitle('View message path')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('prefers stored sender_name for channel messages even when text is not sender-prefixed', () => {
|
||||
render(
|
||||
<MessageList
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
resolvePath,
|
||||
formatDistance,
|
||||
formatHopCounts,
|
||||
formatPathHopWidths,
|
||||
} from '../utils/pathUtils';
|
||||
import type { Contact, RadioConfig } from '../types';
|
||||
import { CONTACT_TYPE_REPEATER } from '../types';
|
||||
@@ -816,3 +817,64 @@ describe('formatHopCounts', () => {
|
||||
expect(result.hasMultiple).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatPathHopWidths', () => {
|
||||
it('returns null for null or empty paths', () => {
|
||||
expect(formatPathHopWidths(null)).toBeNull();
|
||||
expect(formatPathHopWidths([])).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for direct (0-hop) paths', () => {
|
||||
expect(formatPathHopWidths([{ path: '', received_at: 1700000000 }])).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for legacy paths without hop metadata', () => {
|
||||
// No path_len -> width is not derivable, so we show nothing rather than guess.
|
||||
expect(formatPathHopWidths([{ path: 'AABBCCDD', received_at: 1700000000 }])).toBeNull();
|
||||
});
|
||||
|
||||
it('derives 1-byte width', () => {
|
||||
expect(formatPathHopWidths([{ path: '1A2B', path_len: 2, received_at: 1700000000 }])).toBe(
|
||||
'1B'
|
||||
);
|
||||
});
|
||||
|
||||
it('derives 2-byte width', () => {
|
||||
expect(formatPathHopWidths([{ path: 'AABBCCDD', path_len: 2, received_at: 1700000000 }])).toBe(
|
||||
'2B'
|
||||
);
|
||||
});
|
||||
|
||||
it('derives 3-byte width', () => {
|
||||
expect(
|
||||
formatPathHopWidths([{ path: 'AABBCCDDEEFF', path_len: 2, received_at: 1700000000 }])
|
||||
).toBe('3B');
|
||||
});
|
||||
|
||||
it('dedupes identical widths across repeat paths', () => {
|
||||
expect(
|
||||
formatPathHopWidths([
|
||||
{ path: 'AABBCCDD', path_len: 2, received_at: 1700000000 },
|
||||
{ path: '11223344', path_len: 2, received_at: 1700000001 },
|
||||
])
|
||||
).toBe('2B');
|
||||
});
|
||||
|
||||
it('ignores direct paths when other paths have a width', () => {
|
||||
expect(
|
||||
formatPathHopWidths([
|
||||
{ path: '', received_at: 1700000000 },
|
||||
{ path: 'AABBCCDD', path_len: 2, received_at: 1700000001 },
|
||||
])
|
||||
).toBe('2B');
|
||||
});
|
||||
|
||||
it('joins mixed widths sorted ascending', () => {
|
||||
expect(
|
||||
formatPathHopWidths([
|
||||
{ path: 'AABBCCDD', path_len: 2, received_at: 1700000000 }, // 2-byte
|
||||
{ path: '1A2B', path_len: 2, received_at: 1700000001 }, // 1-byte
|
||||
])
|
||||
).toBe('1B/2B');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from '../utils/lastViewedConversation';
|
||||
import { api } from '../api';
|
||||
import { DISTANCE_UNIT_KEY } from '../utils/distanceUnits';
|
||||
import { SHOW_PATH_HOP_WIDTH_KEY } from '../utils/pathHopWidthPreference';
|
||||
import {
|
||||
DEFAULT_FONT_SCALE,
|
||||
FONT_SCALE_KEY,
|
||||
@@ -656,6 +657,19 @@ describe('SettingsModal', () => {
|
||||
expect(localStorage.getItem(LAST_VIEWED_CONVERSATION_KEY)).toBeNull();
|
||||
});
|
||||
|
||||
it('defaults the path-hop-width toggle to off and persists enabling it', () => {
|
||||
renderModal();
|
||||
openLocalSection();
|
||||
|
||||
const checkbox = screen.getByLabelText('Show Path Hop Width');
|
||||
expect(checkbox).not.toBeChecked();
|
||||
expect(localStorage.getItem(SHOW_PATH_HOP_WIDTH_KEY)).toBeNull();
|
||||
|
||||
fireEvent.click(checkbox);
|
||||
|
||||
expect(localStorage.getItem(SHOW_PATH_HOP_WIDTH_KEY)).toBe('true');
|
||||
});
|
||||
|
||||
it('defaults distance units to metric and stores local changes', () => {
|
||||
renderModal();
|
||||
openLocalSection();
|
||||
|
||||
Reference in New Issue
Block a user