139 lines
4.3 KiB
TypeScript
139 lines
4.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import type { StyledBlock } from './tower.component';
|
|
import { editEntryForNewBlock, selectVisibleStyledBlocks } from './tower.component';
|
|
|
|
function block(id: string, opacity = '1', difficulty = 1): StyledBlock {
|
|
return {
|
|
id,
|
|
tag: 'tag',
|
|
description: id,
|
|
is_done: true,
|
|
difficulty,
|
|
created_at: 1,
|
|
_anim: '',
|
|
_transform: opacity === '1' ? 'translateY(0)' : 'translateY(500%)',
|
|
_opacity: opacity,
|
|
};
|
|
}
|
|
|
|
describe('selectVisibleStyledBlocks', () => {
|
|
it('reserves a capped visible slot for the newly completed block', () => {
|
|
const styled = [
|
|
block('newly-done'),
|
|
block('done-1'),
|
|
block('done-2'),
|
|
block('done-3'),
|
|
block('done-4'),
|
|
block('done-5'),
|
|
block('done-6'),
|
|
block('done-7'),
|
|
];
|
|
|
|
const result = selectVisibleStyledBlocks(styled, 6, 'newly-done');
|
|
|
|
expect(result.hiddenCount).toBe(2);
|
|
expect(result.visibleStyled.map((b) => b.id)).toEqual([
|
|
'newly-done',
|
|
'done-3',
|
|
'done-4',
|
|
'done-5',
|
|
'done-6',
|
|
'done-7',
|
|
]);
|
|
});
|
|
|
|
it('uses the normal capped window when no new block is entering', () => {
|
|
const styled = [
|
|
block('done-0'),
|
|
block('done-1'),
|
|
block('done-2'),
|
|
block('done-3'),
|
|
block('done-4'),
|
|
block('done-5'),
|
|
block('done-6'),
|
|
block('done-7'),
|
|
];
|
|
|
|
const result = selectVisibleStyledBlocks(styled, 6, null);
|
|
|
|
expect(result.hiddenCount).toBe(2);
|
|
expect(result.visibleStyled.map((b) => b.id)).toEqual([
|
|
'done-2',
|
|
'done-3',
|
|
'done-4',
|
|
'done-5',
|
|
'done-6',
|
|
'done-7',
|
|
]);
|
|
});
|
|
|
|
it('caps by square cost (difficulty), not by raw block count', () => {
|
|
// Each block draws 2 squares, so only 3 blocks fit in 6 square slots.
|
|
const hard = (id: string): StyledBlock => block(id, '1', 2);
|
|
const styled = [hard('a'), hard('b'), hard('c'), hard('d'), hard('e')];
|
|
|
|
const result = selectVisibleStyledBlocks(styled, 6, null);
|
|
|
|
expect(result.visibleStyled.map((b) => b.id)).toEqual(['c', 'd', 'e']);
|
|
expect(result.hiddenCount).toBe(4);
|
|
});
|
|
|
|
it('keeps a previously-visible block that is now flying out, even on a full stack', () => {
|
|
// Budget is full with three resting blocks; a fourth block has just left the
|
|
// range (opacity 0 → ascending). It was visible a moment ago, so it must stay
|
|
// rendered so its fly-up transition can play instead of vanishing instantly.
|
|
const styled = [
|
|
block('old-1'),
|
|
block('old-2'),
|
|
block('old-3'),
|
|
block('leaving', '0'),
|
|
];
|
|
const prevVisible = new Set(['old-1', 'old-2', 'old-3', 'leaving']);
|
|
|
|
const result = selectVisibleStyledBlocks(styled, 3, null, prevVisible);
|
|
|
|
expect(result.visibleStyled.map((b) => b.id)).toContain('leaving');
|
|
expect(result.hiddenCount).toBe(0);
|
|
});
|
|
|
|
it('does not resurrect an off-screen block that leaves the range', () => {
|
|
// 'hidden-leaving' was never rendered (not in prevVisible) — nothing to
|
|
// animate from, so keep it out and avoid an unbounded phantom render set.
|
|
const styled = [
|
|
block('old-1'),
|
|
block('old-2'),
|
|
block('old-3'),
|
|
block('hidden-leaving', '0'),
|
|
];
|
|
const prevVisible = new Set(['old-1', 'old-2', 'old-3']);
|
|
|
|
const result = selectVisibleStyledBlocks(styled, 3, null, prevVisible);
|
|
|
|
expect(result.visibleStyled.map((b) => b.id)).not.toContain('hidden-leaving');
|
|
});
|
|
|
|
it('counts hidden squares when reserving the entering block', () => {
|
|
const styled = [
|
|
block('newly-done', '1', 3),
|
|
block('done-1', '1', 2),
|
|
block('done-2', '1', 2),
|
|
block('done-3', '1', 2),
|
|
block('done-4', '1', 2),
|
|
];
|
|
|
|
const result = selectVisibleStyledBlocks(styled, 6, 'newly-done');
|
|
|
|
expect(result.hiddenCount).toBe(6);
|
|
expect(result.visibleStyled.map((b) => b.id)).toEqual(['newly-done', 'done-4']);
|
|
});
|
|
});
|
|
|
|
describe('editEntryForNewBlock', () => {
|
|
it('opens the create card in the pending task view when tasks are kept open', () => {
|
|
expect(editEntryForNewBlock(true)).toEqual({ filter: 'pending', activeId: null });
|
|
});
|
|
|
|
it('keeps the existing completed-task default when tasks are collapsed', () => {
|
|
expect(editEntryForNewBlock(false)).toEqual({ filter: 'done', activeId: null });
|
|
});
|
|
});
|