refactor: added new pattern to order parser
This commit is contained in:
parent
723379321d
commit
e76e931c3c
@ -188,6 +188,26 @@ describe('6.4d double dash and plus as a quantity (owner, 5 Nov 2026)', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('6.4f slash as a quantity (owner, 5 Nov 2026)', () => {
|
||||||
|
it('reads / before 1-3 digits after an ending, in every spacing', () => {
|
||||||
|
for (const text of ['1991/2', '1991 / 2', '1991 /2', '1991/ 2']) expect(read(text).lines).toEqual(['1991x2']);
|
||||||
|
expect(read('1991/50 1991/999').lines).toEqual(['1991x50', '1991x999']);
|
||||||
|
});
|
||||||
|
it('4 digits after it, a zero or a second slash stays unreadable', () => {
|
||||||
|
expect(read('0888/0057').unreadable).toEqual(['0888/0057:date_or_time']);
|
||||||
|
expect(read('1991/0').unreadable).toEqual(['1991/0:zero_quantity']);
|
||||||
|
expect(read('2026/11/04').unreadable).toEqual(['2026/11/04:date_or_time']);
|
||||||
|
expect(read('1991/').lines).toEqual([]);
|
||||||
|
});
|
||||||
|
it('a date that does not start with 4 digits is unchanged', () => {
|
||||||
|
expect(read('12/10/2026').unreadable).toEqual(['12/10/2026:date_or_time']);
|
||||||
|
expect(read('0888 12/10')).toMatchObject({ lines: ['0888x1'], unreadable: ['12/10:date_or_time'] });
|
||||||
|
});
|
||||||
|
it('known limit: a year and month (2026/11) reads as ending 2026, quantity 11', () => {
|
||||||
|
expect(read('2026/11').lines).toEqual(['2026x11']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('6.4e colon as a quantity (owner, 5 Nov 2026)', () => {
|
describe('6.4e colon as a quantity (owner, 5 Nov 2026)', () => {
|
||||||
it('reads : before 1-3 digits after an ending, in every spacing', () => {
|
it('reads : before 1-3 digits after an ending, in every spacing', () => {
|
||||||
for (const text of ['1123:5', '1123 : 5', '1123 :5', '1123: 5']) expect(read(text).lines).toEqual(['1123x5']);
|
for (const text of ['1123:5', '1123 : 5', '1123 :5', '1123: 5']) expect(read(text).lines).toEqual(['1123x5']);
|
||||||
|
|||||||
@ -23,7 +23,7 @@ function generate(seed: number): { text: string; expected: Line[] } {
|
|||||||
}
|
}
|
||||||
const ending = pad4(r.int(10000));
|
const ending = pad4(r.int(10000));
|
||||||
const qty = r.int(3) === 0 ? 1 : 1 + r.int(150);
|
const qty = r.int(3) === 0 ? 1 : 1 + r.int(150);
|
||||||
const form = r.pick(['bare', 'x', ' x ', '*', '=', ' = ', '_', '-', '--', ' -- ', '+', ' + ', ':', ' : ', '()', ' ()', 'space'] as const);
|
const form = r.pick(['bare', 'x', ' x ', '*', '=', ' = ', '_', '-', '--', ' -- ', '+', ' + ', ':', ' : ', '/', ' / ', '()', ' ()', 'space'] as const);
|
||||||
const q = String(qty);
|
const q = String(qty);
|
||||||
const rendered =
|
const rendered =
|
||||||
form === 'bare' ? (qty === 1 ? ending : `${ending}x${q}`)
|
form === 'bare' ? (qty === 1 ? ending : `${ending}x${q}`)
|
||||||
|
|||||||
@ -116,6 +116,11 @@ export function scanLine(body: string, atoms: Atom[], line: number): ScanItem[]
|
|||||||
|
|
||||||
if (isOp(a, '.') && !isNoiseDot(k) && b !== undefined && b.kind === 'num') return withQty(i, b.text, k + 2);
|
if (isOp(a, '.') && !isNoiseDot(k) && b !== undefined && b.kind === 'num') return withQty(i, b.text, k + 2);
|
||||||
|
|
||||||
|
if (isOp(a, '/') && b !== undefined && b.kind === 'num') {
|
||||||
|
if (b.text.length > 3) return { ok: false, reason: 'date_or_time', next: k + 2 };
|
||||||
|
return withQty(i, b.text, k + 2);
|
||||||
|
}
|
||||||
|
|
||||||
if (a !== undefined && a.kind === 'op' && QTY_OPS.has(a.text)) {
|
if (a !== undefined && a.kind === 'op' && QTY_OPS.has(a.text)) {
|
||||||
if (b !== undefined && b.kind === 'num') return withQty(i, b.text, k + 2);
|
if (b !== undefined && b.kind === 'num') return withQty(i, b.text, k + 2);
|
||||||
if (a.glued) return { ok: false, reason: 'mixed_characters', next: k + 1 };
|
if (a.glued) return { ok: false, reason: 'mixed_characters', next: k + 1 };
|
||||||
|
|||||||
@ -187,7 +187,7 @@ describe('POST /ingest: replies', () => {
|
|||||||
await say('m1', f.shopUser.phone, '8910=3');
|
await say('m1', f.shopUser.phone, '8910=3');
|
||||||
expect(sent).toEqual([]);
|
expect(sent).toEqual([]);
|
||||||
await say('m2', f.shopUser.phone, '?');
|
await say('m2', f.shopUser.phone, '?');
|
||||||
expect(sent).toEqual([{ chatId: chat, message: '✅ *Got it*\n1 line, 3 tickets so far.\n\n*When the order is finished, reply:*\n1. Done\n2. Cancel' }]);
|
expect(sent).toEqual([{ chatId: chat, message: '✅ *Got it*\n1 number, 3 tickets so far.\n\n*When the order is finished, reply:*\n1. Done\n2. Cancel' }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('plays the whole dialog through /ingest and saves the order', async () => {
|
it('plays the whole dialog through /ingest and saves the order', async () => {
|
||||||
@ -252,7 +252,7 @@ describe('POST /ingest: replies', () => {
|
|||||||
await say('m2', f.shopUser.phone, '?');
|
await say('m2', f.shopUser.phone, '?');
|
||||||
sendFails = false;
|
sendFails = false;
|
||||||
await say('m3', f.shopUser.phone, '?');
|
await say('m3', f.shopUser.phone, '?');
|
||||||
expect(sent.map((s) => s.message)).toEqual(['✅ *Got it*\n1 line, 3 tickets so far.\n\n*When the order is finished, reply:*\n1. Done\n2. Cancel']);
|
expect(sent.map((s) => s.message)).toEqual(['✅ *Got it*\n1 number, 3 tickets so far.\n\n*When the order is finished, reply:*\n1. Done\n2. Cancel']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not wait for the bridge before answering', async () => {
|
it('does not wait for the bridge before answering', async () => {
|
||||||
|
|||||||
@ -458,7 +458,7 @@ describe('photos sent together', () => {
|
|||||||
|
|
||||||
expect(sent).toHaveLength(2);
|
expect(sent).toHaveLength(2);
|
||||||
expect(sent[0]).toContain('Read from your 2 photos');
|
expect(sent[0]).toContain('Read from your 2 photos');
|
||||||
expect(sent[1]).toContain('*Total:* 3 lines, 10 tickets');
|
expect(sent[1]).toContain('*Total:* 3 numbers, 10 tickets');
|
||||||
expect(getConversation(db, f.shopUser.id)?.state).toBe('CONFIRM');
|
expect(getConversation(db, f.shopUser.id)?.state).toBe('CONFIRM');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -58,7 +58,7 @@ describe('the walk-through from the plan (section 3.3)', () => {
|
|||||||
'',
|
'',
|
||||||
'*Shop:* Shop B',
|
'*Shop:* Shop B',
|
||||||
'*Draw:* TODAY 3 Nov',
|
'*Draw:* TODAY 3 Nov',
|
||||||
'*Total:* 2 lines, 4 tickets',
|
'*Total:* 2 numbers, 4 tickets',
|
||||||
'',
|
'',
|
||||||
'*Tickets*',
|
'*Tickets*',
|
||||||
'8910 - 3',
|
'8910 - 3',
|
||||||
@ -79,7 +79,7 @@ describe('the walk-through from the plan (section 3.3)', () => {
|
|||||||
'',
|
'',
|
||||||
'*Shop:* Shop B',
|
'*Shop:* Shop B',
|
||||||
'*Draw:* TODAY 3 Nov',
|
'*Draw:* TODAY 3 Nov',
|
||||||
'*Total:* 3 lines, 6 tickets',
|
'*Total:* 3 numbers, 6 tickets',
|
||||||
'',
|
'',
|
||||||
'*Tickets*',
|
'*Tickets*',
|
||||||
'8910 - 3',
|
'8910 - 3',
|
||||||
@ -208,13 +208,13 @@ describe('collecting', () => {
|
|||||||
const say = chatAs(f.shopUser.id);
|
const say = chatAs(f.shopUser.id);
|
||||||
say('8910=3');
|
say('8910=3');
|
||||||
const [summary] = say('7810=1\ndone');
|
const [summary] = say('7810=1\ndone');
|
||||||
expect(summary).toContain('2 lines, 4 tickets');
|
expect(summary).toContain('2 numbers, 4 tickets');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('a whole order with done in the very first message needs no menu', () => {
|
it('a whole order with done in the very first message needs no menu', () => {
|
||||||
const say = chatAs(f.shopUser.id);
|
const say = chatAs(f.shopUser.id);
|
||||||
const [summary] = say('0888\n0057\ndone');
|
const [summary] = say('0888\n0057\ndone');
|
||||||
expect(summary).toContain('2 lines, 2 tickets');
|
expect(summary).toContain('2 numbers, 2 tickets');
|
||||||
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -222,7 +222,7 @@ describe('collecting', () => {
|
|||||||
const say = chatAs(f.shopUser.id);
|
const say = chatAs(f.shopUser.id);
|
||||||
say('8910=3');
|
say('8910=3');
|
||||||
const [summary] = say('0888=2\n7810=1\n1');
|
const [summary] = say('0888=2\n7810=1\n1');
|
||||||
expect(summary).toContain('3 lines, 6 tickets');
|
expect(summary).toContain('3 numbers, 6 tickets');
|
||||||
expect(summary).toContain('✅ Everything was read');
|
expect(summary).toContain('✅ Everything was read');
|
||||||
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
||||||
});
|
});
|
||||||
@ -303,7 +303,7 @@ describe('unreadable text', () => {
|
|||||||
expect(again).toContain('⚠️ *Not read, not in the order:*\n 0x38=1');
|
expect(again).toContain('⚠️ *Not read, not in the order:*\n 0x38=1');
|
||||||
expect(again).toContain('⚠️ *Left out earlier, not in the order:*\n 08?8=2');
|
expect(again).toContain('⚠️ *Left out earlier, not in the order:*\n 08?8=2');
|
||||||
// the new line is unreadable too, so nothing was added to the order
|
// the new line is unreadable too, so nothing was added to the order
|
||||||
expect(again).toContain('1 line, 3 tickets');
|
expect(again).toContain('1 number, 3 ticket');
|
||||||
expect(again).not.toContain('Everything was read');
|
expect(again).not.toContain('Everything was read');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ describe('unreadable text', () => {
|
|||||||
say('done');
|
say('done');
|
||||||
say('2');
|
say('2');
|
||||||
const [again] = say('1234=1'); // a forgotten line, not a correction
|
const [again] = say('1234=1'); // a forgotten line, not a correction
|
||||||
expect(again).toContain('2 lines, 4 tickets');
|
expect(again).toContain('2 numbers, 4 tickets');
|
||||||
expect(again).toContain('⚠️ *Left out earlier, not in the order:*\n 08?8=2');
|
expect(again).toContain('⚠️ *Left out earlier, not in the order:*\n 08?8=2');
|
||||||
expect(again).not.toContain('Everything was read');
|
expect(again).not.toContain('Everything was read');
|
||||||
say('1');
|
say('1');
|
||||||
@ -329,7 +329,7 @@ describe('unreadable text', () => {
|
|||||||
say('done');
|
say('done');
|
||||||
say('2');
|
say('2');
|
||||||
const [again] = say('1234=1');
|
const [again] = say('1234=1');
|
||||||
expect(again).toContain('*Total:* 2 lines, 4 tickets\n✅ Everything was read');
|
expect(again).toContain('*Total:* 2 numbers, 4 tickets\n✅ Everything was read');
|
||||||
expect(again).not.toContain('Left out earlier');
|
expect(again).not.toContain('Left out earlier');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -363,7 +363,7 @@ describe('repeated endings', () => {
|
|||||||
const say = chatAs(f.shopUser.id);
|
const say = chatAs(f.shopUser.id);
|
||||||
say('8910=3\n7810=1\n8910=3');
|
say('8910=3\n7810=1\n8910=3');
|
||||||
const [summary] = say('done');
|
const [summary] = say('done');
|
||||||
expect(summary).toContain('3 lines, 7 tickets');
|
expect(summary).toContain('3 numbers, 7 tickets');
|
||||||
expect(summary).toContain('8910 - 6 (2 times)');
|
expect(summary).toContain('8910 - 6 (2 times)');
|
||||||
expect(summary).toContain('7810 - 1');
|
expect(summary).toContain('7810 - 1');
|
||||||
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
||||||
@ -399,7 +399,7 @@ describe('the menus', () => {
|
|||||||
say('done');
|
say('done');
|
||||||
expect(say('2')).toEqual([msg.addPrompt()]);
|
expect(say('2')).toEqual([msg.addPrompt()]);
|
||||||
const [summary] = say('7810=1');
|
const [summary] = say('7810=1');
|
||||||
expect(summary).toContain('2 lines, 4 tickets');
|
expect(summary).toContain('2 numbers, 4 tickets');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('1 and 3 keep their meaning while an Add more is pending', () => {
|
it('1 and 3 keep their meaning while an Add more is pending', () => {
|
||||||
@ -426,7 +426,7 @@ describe('the menus', () => {
|
|||||||
const [reply] = say('7810=1');
|
const [reply] = say('7810=1');
|
||||||
|
|
||||||
expect(reply).toContain('*Order summary*');
|
expect(reply).toContain('*Order summary*');
|
||||||
expect(reply).toContain('*Total:* 2 lines, 4 tickets');
|
expect(reply).toContain('*Total:* 2 numbers, 4 tickets');
|
||||||
expect(reply).toContain('7810 - 1');
|
expect(reply).toContain('7810 - 1');
|
||||||
expect(reply).toContain(SUMMARY_MENU);
|
expect(reply).toContain(SUMMARY_MENU);
|
||||||
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
||||||
@ -444,7 +444,7 @@ describe('the menus', () => {
|
|||||||
say('8910=3');
|
say('8910=3');
|
||||||
say('done');
|
say('done');
|
||||||
expect(say('2')).toEqual([msg.addPrompt()]);
|
expect(say('2')).toEqual([msg.addPrompt()]);
|
||||||
expect(say('7810=1')[0]).toContain('*Total:* 2 lines, 4 tickets');
|
expect(say('7810=1')[0]).toContain('*Total:* 2 numbers, 4 tickets');
|
||||||
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
expect(state(f.shopUser.id)).toBe('CONFIRM');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -225,7 +225,7 @@ describe('a readable photo', () => {
|
|||||||
|
|
||||||
const replies = photo(id, '9001 2');
|
const replies = photo(id, '9001 2');
|
||||||
expect(replies[0]).toBe(msg.photoAdded('9001 2'));
|
expect(replies[0]).toBe(msg.photoAdded('9001 2'));
|
||||||
expect(replies[1]).toContain('*Total:* 2 lines, 7 tickets');
|
expect(replies[1]).toContain('*Total:* 2 numbers, 7 tickets');
|
||||||
expect(conv(id)?.state).toBe('CONFIRM');
|
expect(conv(id)?.state).toBe('CONFIRM');
|
||||||
expect(orders()).toEqual([]);
|
expect(orders()).toEqual([]);
|
||||||
|
|
||||||
@ -322,7 +322,7 @@ describe('a photo with no ticket lines', () => {
|
|||||||
const [reply] = photo(id, slip);
|
const [reply] = photo(id, slip);
|
||||||
expect(reply).toBe(msg.photoNoTickets(slip, notRead, msg.photoOpenOrder(1, 5)));
|
expect(reply).toBe(msg.photoNoTickets(slip, notRead, msg.photoOpenOrder(1, 5)));
|
||||||
expect(reply).not.toContain('Got it');
|
expect(reply).not.toContain('Got it');
|
||||||
expect(reply).toContain('1 line, 5 tickets so far.');
|
expect(reply).toContain('1 number, 5 tickets so far.');
|
||||||
expect(conv(id)).toMatchObject({ state: 'COLLECTING', buffer_text: '6379 5', source: 'whatsapp_text' });
|
expect(conv(id)).toMatchObject({ state: 'COLLECTING', buffer_text: '6379 5', source: 'whatsapp_text' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -56,7 +56,7 @@ describe('summary', () => {
|
|||||||
'',
|
'',
|
||||||
'*Shop:* Shop B',
|
'*Shop:* Shop B',
|
||||||
'*Draw:* TODAY 3 Nov',
|
'*Draw:* TODAY 3 Nov',
|
||||||
'*Total:* 2 lines, 4 tickets',
|
'*Total:* 2 numbers, 4 tickets',
|
||||||
'',
|
'',
|
||||||
'*Tickets*',
|
'*Tickets*',
|
||||||
'8910 - 3',
|
'8910 - 3',
|
||||||
@ -84,7 +84,7 @@ describe('summary', () => {
|
|||||||
'',
|
'',
|
||||||
'*Shop:* Shop B',
|
'*Shop:* Shop B',
|
||||||
'*Draw:* TODAY 3 Nov',
|
'*Draw:* TODAY 3 Nov',
|
||||||
'*Total:* 3 lines, 6 tickets',
|
'*Total:* 3 numbers, 6 tickets',
|
||||||
'✅ Everything was read',
|
'✅ Everything was read',
|
||||||
'',
|
'',
|
||||||
'*Tickets*',
|
'*Tickets*',
|
||||||
@ -105,7 +105,7 @@ describe('summary', () => {
|
|||||||
'',
|
'',
|
||||||
'*Shop:* Shop B',
|
'*Shop:* Shop B',
|
||||||
'*Draw:* TODAY 3 Nov',
|
'*Draw:* TODAY 3 Nov',
|
||||||
'*Total:* 3 lines, 6 tickets',
|
'*Total:* 3 numbers, 6 tickets',
|
||||||
'',
|
'',
|
||||||
'*Tickets*',
|
'*Tickets*',
|
||||||
'8910 - 3',
|
'8910 - 3',
|
||||||
@ -146,7 +146,7 @@ describe('summary', () => {
|
|||||||
entries: [{ ending: '0001', qty: 1, times: 1 }],
|
entries: [{ ending: '0001', qty: 1, times: 1 }],
|
||||||
unreadable: [],
|
unreadable: [],
|
||||||
});
|
});
|
||||||
expect(t).toContain('*Total:* 1 line, 1 ticket\n✅ Everything was read');
|
expect(t).toContain('*Total:* 1 number, 1 ticket\n✅ Everything was read');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('caps a long unreadable list and counts the rest', () => {
|
it('caps a long unreadable list and counts the rest', () => {
|
||||||
@ -165,7 +165,7 @@ describe('summary', () => {
|
|||||||
|
|
||||||
describe('prompts', () => {
|
describe('prompts', () => {
|
||||||
it('the collecting reply gives the totals and offers Done and Cancel', () => {
|
it('the collecting reply gives the totals and offers Done and Cancel', () => {
|
||||||
expect(m.progressUpdate(1, 3)).toBe('✅ *Got it*\n1 line, 3 tickets so far.\n\n*When the order is finished, reply:*\n1. Done\n2. Cancel');
|
expect(m.progressUpdate(1, 3)).toBe('✅ *Got it*\n1 number, 3 tickets so far.\n\n*When the order is finished, reply:*\n1. Done\n2. Cancel');
|
||||||
});
|
});
|
||||||
it('shop list is numbered in the order given', () => {
|
it('shop list is numbered in the order given', () => {
|
||||||
expect(m.shopList(['Shop A', 'Shop B'])).toBe('*Which shop?*\n\n1. Shop A\n2. Shop B');
|
expect(m.shopList(['Shop A', 'Shop B'])).toBe('*Which shop?*\n\n1. Shop A\n2. Shop B');
|
||||||
@ -184,7 +184,7 @@ describe('prompts', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('reminder shows progress when something was read', () => {
|
it('reminder shows progress when something was read', () => {
|
||||||
expect(m.reminder(5, 12)).toBe('⏳ *Unfinished order*\n5 lines, 12 tickets so far.\n\n*Reply:*\n1. Done\n2. Cancel');
|
expect(m.reminder(5, 12)).toBe('⏳ *Unfinished order*\n5 numbers, 12 tickets so far.\n\n*Reply:*\n1. Done\n2. Cancel');
|
||||||
});
|
});
|
||||||
it('reminder omits progress when nothing was read', () => {
|
it('reminder omits progress when nothing was read', () => {
|
||||||
expect(m.reminder(0, 0)).toBe('⏳ *Unfinished order*\n\n*Reply:*\n1. Done\n2. Cancel');
|
expect(m.reminder(0, 0)).toBe('⏳ *Unfinished order*\n\n*Reply:*\n1. Done\n2. Cancel');
|
||||||
@ -193,7 +193,7 @@ describe('prompts', () => {
|
|||||||
expect(m.splitQuestion(5, 12)).toBe(
|
expect(m.splitQuestion(5, 12)).toBe(
|
||||||
[
|
[
|
||||||
'⏳ *Unfinished order*',
|
'⏳ *Unfinished order*',
|
||||||
'5 lines, 12 tickets so far.',
|
'5 numbers, 12 tickets so far.',
|
||||||
'',
|
'',
|
||||||
'*Is this message:*',
|
'*Is this message:*',
|
||||||
'1. Part of that order',
|
'1. Part of that order',
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export const SUMMARY_MENU = '*Reply:*\n1. Save\n2. Add more\n3. Cancel';
|
|||||||
const FINISH_PROMPT = `*When the order is finished, reply:*\n${COLLECTING_MENU}`;
|
const FINISH_PROMPT = `*When the order is finished, reply:*\n${COLLECTING_MENU}`;
|
||||||
|
|
||||||
function soFar(lineCount: number, ticketCount: number): string[] {
|
function soFar(lineCount: number, ticketCount: number): string[] {
|
||||||
return lineCount > 0 ? [`${plural(lineCount, 'line')}, ${plural(ticketCount, 'ticket')} so far.`] : [];
|
return lineCount > 0 ? [`${plural(lineCount, 'number')}, ${plural(ticketCount, 'ticket')} so far.`] : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function progressUpdate(lineCount: number, ticketCount: number): string {
|
export function progressUpdate(lineCount: number, ticketCount: number): string {
|
||||||
@ -92,7 +92,7 @@ export function summary(s: SummaryInput): string {
|
|||||||
'',
|
'',
|
||||||
`*Shop:* ${s.shopName}`,
|
`*Shop:* ${s.shopName}`,
|
||||||
`*Draw:* ${drawLabel(s.drawDate, s.today)}`,
|
`*Draw:* ${drawLabel(s.drawDate, s.today)}`,
|
||||||
`*Total:* ${plural(s.lineCount, 'line')}, ${plural(s.ticketCount, 'ticket')}`,
|
`*Total:* ${plural(s.lineCount, 'number')}, ${plural(s.ticketCount, 'ticket')}`,
|
||||||
];
|
];
|
||||||
if (s.unreadable.length === 0 && s.leftOut.length === 0) out.push('✅ Everything was read');
|
if (s.unreadable.length === 0 && s.leftOut.length === 0) out.push('✅ Everything was read');
|
||||||
out.push('', '*Tickets*');
|
out.push('', '*Tickets*');
|
||||||
|
|||||||
@ -66,7 +66,7 @@ describe('reminder', () => {
|
|||||||
|
|
||||||
expect(await sweepAt(after(10))).toEqual({ reminded: 1, expired: 0 });
|
expect(await sweepAt(after(10))).toEqual({ reminded: 1, expired: 0 });
|
||||||
expect(sent).toEqual([{ chatId: 'chat-1@lid', message: msg.reminder(2, 4) }]);
|
expect(sent).toEqual([{ chatId: 'chat-1@lid', message: msg.reminder(2, 4) }]);
|
||||||
expect(sent[0]!.message).toBe('⏳ *Unfinished order*\n2 lines, 4 tickets so far.\n\n*Reply:*\n1. Done\n2. Cancel');
|
expect(sent[0]!.message).toBe('⏳ *Unfinished order*\n2 numbers, 4 tickets so far.\n\n*Reply:*\n1. Done\n2. Cancel');
|
||||||
|
|
||||||
expect(await sweepAt(after(11))).toEqual({ reminded: 0, expired: 0 });
|
expect(await sweepAt(after(11))).toEqual({ reminded: 0, expired: 0 });
|
||||||
expect(await sweepAt(after(20))).toEqual({ reminded: 0, expired: 0 });
|
expect(await sweepAt(after(20))).toEqual({ reminded: 0, expired: 0 });
|
||||||
@ -284,8 +284,8 @@ describe('the reply after a pause', () => {
|
|||||||
|
|
||||||
it('words it with the running totals and the menu', () => {
|
it('words it with the running totals and the menu', () => {
|
||||||
const menu = '\n\n*When the order is finished, reply:*\n1. Done\n2. Cancel';
|
const menu = '\n\n*When the order is finished, reply:*\n1. Done\n2. Cancel';
|
||||||
expect(msg.progressUpdate(8, 20)).toBe('✅ *Got it*\n8 lines, 20 tickets so far.' + menu);
|
expect(msg.progressUpdate(8, 20)).toBe('✅ *Got it*\n8 numbers, 20 tickets so far.' + menu);
|
||||||
expect(msg.progressUpdate(1, 1)).toBe('✅ *Got it*\n1 line, 1 ticket so far.' + menu);
|
expect(msg.progressUpdate(1, 1)).toBe('✅ *Got it*\n1 number, 1 ticket so far.' + menu);
|
||||||
expect(msg.progressUpdate(0, 0)).toBe('✅ *Got it*' + menu);
|
expect(msg.progressUpdate(0, 0)).toBe('✅ *Got it*' + menu);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
10
test/fixtures/orders/slash-and-equals-2026-11-05.expected.json
vendored
Normal file
10
test/fixtures/orders/slash-and-equals-2026-11-05.expected.json
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"totalTickets": 3,
|
||||||
|
"lines": [
|
||||||
|
"1991 x2 @1",
|
||||||
|
"3267 x1 @2"
|
||||||
|
],
|
||||||
|
"repeated": [],
|
||||||
|
"unreadable": [],
|
||||||
|
"ignored": []
|
||||||
|
}
|
||||||
2
test/fixtures/orders/slash-and-equals-2026-11-05.txt
vendored
Normal file
2
test/fixtures/orders/slash-and-equals-2026-11-05.txt
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
1991/2
|
||||||
|
3267=1
|
||||||
Loading…
Reference in New Issue
Block a user