Files
daily_console_web/src/features/store-admin/counterRows.test.ts
2026-09-03 16:47:04 +05:30

100 lines
4.1 KiB
TypeScript

import { strict as assert } from 'node:assert';
import { test } from 'node:test';
import type { TerminalStatus } from './posStatus';
import { byUrgency, countsOf, filterRows, stateOf, type CounterRow } from './counterRows';
const status = (over: Partial<TerminalStatus> = {}): TerminalStatus =>
({
terminalId: 'T1', storeName: 'R mart', presence: 'online', sync: 'synced', state: 'synced',
pendingBills: 0, oldestPendingMs: 0, oldestPendingAt: null, silentForMs: 30_000,
todayBills: 4, todayAmount: 450, lastBillAt: new Date(), reportedAt: null, receivedAt: null,
clockDriftSeconds: null, reason: '',
...over,
}) as TerminalStatus;
/*
Four states, and the one that matters is the split between "never used" and
"offline". Read only the heartbeat and they look identical — but one is a
counter nobody has switched on yet, and the other is a shop losing sales right
now. The old page put them in one bucket, which diluted the urgent case with
the routine one.
*/
test('a counter that has never sold and never reported is "never used"', () => {
assert.equal(stateOf(status({ lastBillAt: null, todayBills: 0, silentForMs: null }), false), 'never');
});
test('a counter that HAS sold before but is silent now is offline, not "never used"', () => {
// The distinction the page exists to make.
assert.equal(
stateOf(status({ state: 'offline', lastBillAt: new Date('2026-09-01'), todayBills: 0 }), false),
'offline',
);
});
test('a stale counter counts as offline', () => {
// Its last word was "fine" and everything since is guesswork.
assert.equal(stateOf(status({ state: 'stale' }), false), 'offline');
});
test('unsent bills put a healthy counter into attention', () => {
// Connected and holding sales is exactly what makes revenue read low, and it
// is invisible if only the connection state is read.
assert.equal(stateOf(status({ pendingBills: 3 }), false), 'attention');
});
test('a named problem puts a counter into attention even when it looks fine', () => {
assert.equal(stateOf(status(), true), 'attention');
});
test('a counter with no status at all is "never used", not a crash', () => {
assert.equal(stateOf(null, false), 'never');
});
/* ── Counting and filtering ───────────────────────────────────────────────── */
const row = (key: string, over: Partial<CounterRow> = {}): CounterRow => ({
key, terminalId: key, branchId: 1, branchName: 'R mart', state: 'active',
status: status(), periodBills: 0, periodAmount: 0, card: null,
...over,
});
test('every counter is counted exactly once', () => {
const counts = countsOf([
row('a'), row('b'), row('c', { state: 'attention' }), row('d', { state: 'offline' }),
]);
assert.deepEqual(counts, { all: 4, active: 2, attention: 1, never: 0, offline: 1 });
});
test('search finds a counter by the code printed on its receipts', () => {
// Usually the only identifier the person standing at it can read.
const rows = [row('T5ED0'), row('T8719')];
const found = filterRows(rows, { state: 'all', branchId: null, term: 't5ed', nameFor: () => '' });
assert.equal(found.length, 1);
assert.equal(found[0]?.terminalId, 'T5ED0');
});
test('search also finds it by the name the operator gave it', () => {
const rows = [row('T5ED0'), row('T8719')];
const found = filterRows(rows, {
state: 'all', branchId: null, term: 'main',
nameFor: (id) => (id === 'T5ED0' ? 'Main Counter' : 'Counter 2'),
});
assert.equal(found[0]?.terminalId, 'T5ED0');
});
test('a branch filter and a state filter both apply', () => {
const rows = [
row('a', { branchId: 1, state: 'offline' }),
row('b', { branchId: 2, state: 'offline' }),
row('c', { branchId: 1, state: 'active' }),
];
const found = filterRows(rows, { state: 'offline', branchId: 1, term: '', nameFor: () => '' });
assert.deepEqual(found.map((r) => r.key), ['a']);
});
test('the worst counters sort to the top', () => {
const sorted = [row('a'), row('b', { state: 'offline' }), row('c', { state: 'attention' })].sort(byUrgency);
assert.deepEqual(sorted.map((r) => r.state), ['offline', 'attention', 'active']);
});