Files
Remote-Terminal-for-MeshCore/frontend/src/test/repeaterMode.test.ts
T
2026-01-09 23:08:55 -08:00

133 lines
4.5 KiB
TypeScript

/**
* Tests for repeater-specific behavior.
*
* These tests verify edge cases in repeater interactions that could easily
* regress if the code is modified:
*
* 1. Repeater messages should NOT have sender parsed from text (colons are common in CLI output)
* 2. Password field "." should convert to empty string (for repeaters with no password)
*/
import { describe, it, expect } from 'vitest';
import { parseSenderFromText } from '../utils/messageParser';
import { CONTACT_TYPE_REPEATER, CONTACT_TYPE_CLIENT } from '../types';
describe('Repeater message sender parsing', () => {
/**
* CLI responses from repeaters often contain colons (e.g., "clock: 12:30:00").
* If we parse these like normal channel messages, we'd incorrectly extract
* "clock" as a sender name, breaking the display.
*
* The fix in MessageList.tsx is to check if the contact is a repeater and
* skip parseSenderFromText entirely. These tests document the expected
* behavior pattern.
*/
it('parseSenderFromText would incorrectly parse CLI responses with colons', () => {
// This demonstrates WHY we skip parsing for repeaters
const cliResponse = 'clock: 2024-01-09 12:30:00';
const parsed = parseSenderFromText(cliResponse);
// Without the repeater check, we'd get this incorrect result:
expect(parsed.sender).toBe('clock');
expect(parsed.content).toBe('2024-01-09 12:30:00');
// This would display as "clock" sent "2024-01-09 12:30:00" - WRONG!
});
it('repeater messages should bypass parsing entirely', () => {
// This documents the correct behavior: skip parsing for repeaters
const cliResponse = 'clock: 2024-01-09 12:30:00';
const contactType = CONTACT_TYPE_REPEATER;
// The pattern used in MessageList.tsx:
const isRepeater = contactType === CONTACT_TYPE_REPEATER;
const { sender, content } = isRepeater
? { sender: null, content: cliResponse }
: parseSenderFromText(cliResponse);
// Correct: full text preserved, no sender extracted
expect(sender).toBeNull();
expect(content).toBe('clock: 2024-01-09 12:30:00');
});
it('non-repeater messages still get sender parsed', () => {
const channelMessage = 'Alice: Hello everyone!';
const contactType = CONTACT_TYPE_CLIENT;
const isRepeater = contactType === CONTACT_TYPE_REPEATER;
const { sender, content } = isRepeater
? { sender: null, content: channelMessage }
: parseSenderFromText(channelMessage);
// Normal behavior: sender extracted
expect(sender).toBe('Alice');
expect(content).toBe('Hello everyone!');
});
it('handles various CLI response formats that would be mis-parsed', () => {
const cliResponses = [
'ver: 1.2.3',
'tx: 20 dBm',
'name: MyRepeater',
'radio: 915.0,125,9,5',
'Error: command not found',
'uptime: 3d 12h 30m',
];
for (const response of cliResponses) {
// All of these would be incorrectly parsed without the repeater check
const parsed = parseSenderFromText(response);
expect(parsed.sender).not.toBeNull();
// But with repeater check, they're preserved
const isRepeater = true;
const { sender, content } = isRepeater
? { sender: null, content: response }
: parseSenderFromText(response);
expect(sender).toBeNull();
expect(content).toBe(response);
}
});
});
describe('Repeater password handling', () => {
/**
* The "." password convention allows users to specify an empty password
* for repeaters that don't require authentication. Without this, users
* couldn't submit an empty password through the form.
*/
it('"." converts to empty password', () => {
// This is the logic in MessageInput.tsx handleSubmit
const trimmed = '.';
const password = trimmed === '.' ? '' : trimmed;
expect(password).toBe('');
});
it('normal password is passed through unchanged', () => {
const trimmed = 'mySecretPassword';
const password = trimmed === '.' ? '' : trimmed;
expect(password).toBe('mySecretPassword');
});
it('"." with surrounding whitespace still works after trim', () => {
// In MessageInput, text.trim() is called before the check
const text = ' . ';
const trimmed = text.trim();
const password = trimmed === '.' ? '' : trimmed;
expect(password).toBe('');
});
it('".." is NOT converted (only single dot)', () => {
const trimmed = '..';
const password = trimmed === '.' ? '' : trimmed;
// Double dot is passed through as-is (it's a valid password)
expect(password).toBe('..');
});
});