5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 1 | import assert from 'node:assert/strict'; |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 2 | import { test } from 'node:test'; |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 3 | import { dropSlot, moveBy, moveToSlot, type Box } from '../src/lib/reorder-model.ts'; |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 4 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 5 | const box = (left: number, top: number): Box => ({ left, top, width: 30, height: 24 }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 6 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 7 | const wrapped = [box(0, 0), box(34, 0), box(68, 0), box(0, 28), box(34, 28)]; |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 8 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 9 | test('a pointer left of a chip centre lands before it', () => { |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 10 | assert.deepEqual(dropSlot(wrapped, 40, 10), { slot: 1, after: false }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 11 | assert.deepEqual(dropSlot(wrapped, 2, 10), { slot: 0, after: false }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 12 | }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 13 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 14 | test('a pointer right of every chip in a row lands after its last chip', () => { |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 15 | assert.deepEqual(dropSlot(wrapped, 200, 10), { slot: 3, after: true }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 16 | assert.deepEqual(dropSlot(wrapped, 200, 30), { slot: 5, after: true }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 17 | }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 18 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 19 | test('the nearest row wins when the pointer is above or below the chips', () => { |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 20 | assert.deepEqual(dropSlot(wrapped, 40, -50), { slot: 1, after: false }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 21 | assert.deepEqual(dropSlot(wrapped, 40, 90), { slot: 4, after: false }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 22 | assert.deepEqual(dropSlot(wrapped, 10, 27), { slot: 3, after: false }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 23 | }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 24 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 25 | test('chips of slightly different heights share a row', () => { |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 26 | const mixed = [box(0, 0), { left: 34, top: 2, width: 12, height: 20 }, box(50, 0)]; |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 27 | assert.deepEqual(dropSlot(mixed, 100, 10), { slot: 3, after: true }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 28 | }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 29 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 30 | test('an empty list always lands at the start', () => { |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 31 | assert.deepEqual(dropSlot([], 10, 10), { slot: 0, after: false }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 32 | }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 33 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 34 | test('moving to a slot after the source accounts for the removed item', () => { |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 35 | assert.deepEqual(moveToSlot(['a', 'b', 'c', 'd'], 0, 3), ['b', 'c', 'a', 'd']); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 36 | assert.deepEqual(moveToSlot(['a', 'b', 'c', 'd'], 0, 4), ['b', 'c', 'd', 'a']); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 37 | assert.deepEqual(moveToSlot(['a', 'b', 'c', 'd'], 3, 1), ['a', 'd', 'b', 'c']); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 38 | }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 39 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 40 | test('moving onto its own slot returns the same list', () => { |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 41 | const list = ['a', 'b', 'c']; |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 42 | assert.equal(moveToSlot(list, 1, 1), list); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 43 | assert.equal(moveToSlot(list, 1, 2), list); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 44 | assert.equal(moveToSlot(list, 5, 0), list); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 45 | assert.equal(moveToSlot(list, -1, 0), list); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 46 | }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 47 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 48 | test('moving never mutates the input and clamps the slot', () => { |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 49 | const list = ['a', 'b', 'c']; |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 50 | assert.deepEqual(moveToSlot(list, 0, 99), ['b', 'c', 'a']); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 51 | assert.deepEqual(moveToSlot(list, 2, -4), ['c', 'a', 'b']); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 52 | assert.deepEqual(list, ['a', 'b', 'c']); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 53 | }); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 54 | |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 55 | test('moving by a step swaps neighbours and stops at the ends', () => { |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 56 | const list = ['a', 'b', 'c']; |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 57 | assert.deepEqual(moveBy(list, 0, 1), ['b', 'a', 'c']); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 58 | assert.deepEqual(moveBy(list, 2, -1), ['a', 'c', 'b']); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 59 | assert.equal(moveBy(list, 0, -1), list); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 60 | assert.equal(moveBy(list, 2, 1), list); |
5059ed753f8b | atagen | 2026-10-27 07:20 UTC | 61 | }); |