forked from eva/focus-flow
test(timeline): add timeline state regression suite
This commit is contained in:
parent
88434e3805
commit
abf069db1f
4 changed files with 171 additions and 5 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# V1 Block 08 — Today Timeline State
|
||||
|
||||
Status: In progress — Chunks 8.1, 8.2, and 8.3 complete
|
||||
Status: Complete
|
||||
|
||||
Purpose: Prepare domain/view-state data for the Today timeline without building full Flutter UI yet unless explicitly requested.
|
||||
|
||||
|
|
@ -149,6 +149,15 @@ Acceptance criteria:
|
|||
- Mapping rules are tested without depending on a UI framework.
|
||||
- Existing scheduling, backlog, locked-block, and task-action tests still pass.
|
||||
|
||||
Completed:
|
||||
|
||||
- Expanded timeline regression coverage for all task-type background tokens.
|
||||
- Added exhaustive reward icon token coverage for every reward level plus `notSet`.
|
||||
- Added exhaustive difficulty icon token coverage for every difficulty level plus `notSet`.
|
||||
- Added focused compact-mode reduced-output regression coverage.
|
||||
- Confirmed existing timeline tests cover flexible duration display, required explicit time display, reveal-gated locked overlays, locked overlay category, project color tokens, and flexible quick actions.
|
||||
- Verified with `dart format lib test`, `dart analyze`, and `dart test`.
|
||||
|
||||
Commit suggestion:
|
||||
|
||||
```text
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# V1 Block 08 — Today Timeline State
|
||||
|
||||
Status: In progress — Chunks 8.1, 8.2, and 8.3 complete
|
||||
Status: Complete
|
||||
|
||||
Purpose: Prepare domain/view-state data for the Today timeline without building full Flutter UI yet unless explicitly requested.
|
||||
|
||||
|
|
@ -149,6 +149,15 @@ Acceptance criteria:
|
|||
- Mapping rules are tested without depending on a UI framework.
|
||||
- Existing scheduling, backlog, locked-block, and task-action tests still pass.
|
||||
|
||||
Completed:
|
||||
|
||||
- Expanded timeline regression coverage for all task-type background tokens.
|
||||
- Added exhaustive reward icon token coverage for every reward level plus `notSet`.
|
||||
- Added exhaustive difficulty icon token coverage for every difficulty level plus `notSet`.
|
||||
- Added focused compact-mode reduced-output regression coverage.
|
||||
- Confirmed existing timeline tests cover flexible duration display, required explicit time display, reveal-gated locked overlays, locked overlay category, project color tokens, and flexible quick actions.
|
||||
- Verified with `dart format lib test`, `dart analyze`, and `dart test`.
|
||||
|
||||
Commit suggestion:
|
||||
|
||||
```text
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Current Software Plan
|
||||
|
||||
Execute active V1 block documents in numeric order. Blocks 01-07 are completed
|
||||
and archived; the next active block is Block 08.
|
||||
Execute active V1 block documents in numeric order. Blocks 01-08 are completed
|
||||
and archived; the next active block is Block 09.
|
||||
|
||||
## Execution rules
|
||||
|
||||
|
|
@ -33,6 +33,6 @@ Do not infer a new level name.
|
|||
5. `V1_BLOCK_05_Recurring_Locked_Blocks.md`
|
||||
6. `V1_BLOCK_06_Task_Actions_State_Transitions_UPDATED.md` — archived
|
||||
7. `V1_BLOCK_07_Child_Tasks.md` — archived
|
||||
8. `V1_BLOCK_08_Today_Timeline_State.md`
|
||||
8. `V1_BLOCK_08_Today_Timeline_State.md` — archived
|
||||
9. `V1_BLOCK_09_Persistence_Preparation.md`
|
||||
10. `V1_BLOCK_10_Testing_Documentation_Handoff.md`
|
||||
|
|
|
|||
|
|
@ -44,6 +44,96 @@ void main() {
|
|||
]);
|
||||
});
|
||||
|
||||
test('background token maps from every task type', () {
|
||||
final expectedTokens = {
|
||||
TaskType.flexible: TimelineBackgroundToken.flexible,
|
||||
TaskType.inflexible: TimelineBackgroundToken.inflexible,
|
||||
TaskType.critical: TimelineBackgroundToken.critical,
|
||||
TaskType.locked: TimelineBackgroundToken.locked,
|
||||
TaskType.surprise: TimelineBackgroundToken.surprise,
|
||||
TaskType.freeSlot: TimelineBackgroundToken.freeSlot,
|
||||
};
|
||||
|
||||
for (final entry in expectedTokens.entries) {
|
||||
final task = scheduledTask(
|
||||
id: entry.key.name,
|
||||
title: entry.key.name,
|
||||
projectId: project.id,
|
||||
type: entry.key,
|
||||
createdAt: createdAt,
|
||||
start: start,
|
||||
end: end,
|
||||
);
|
||||
|
||||
expect(
|
||||
mapper.fromTask(task).backgroundToken,
|
||||
entry.value,
|
||||
reason: entry.key.name,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('reward icon token handles every reward level plus not set', () {
|
||||
final expectedTokens = {
|
||||
RewardLevel.notSet: TimelineRewardIconToken.notSet,
|
||||
RewardLevel.veryLow: TimelineRewardIconToken.veryLow,
|
||||
RewardLevel.low: TimelineRewardIconToken.low,
|
||||
RewardLevel.medium: TimelineRewardIconToken.medium,
|
||||
RewardLevel.high: TimelineRewardIconToken.high,
|
||||
RewardLevel.veryHigh: TimelineRewardIconToken.veryHigh,
|
||||
};
|
||||
|
||||
for (final entry in expectedTokens.entries) {
|
||||
final task = scheduledTask(
|
||||
id: entry.key.name,
|
||||
title: entry.key.name,
|
||||
projectId: project.id,
|
||||
type: TaskType.flexible,
|
||||
reward: entry.key,
|
||||
createdAt: createdAt,
|
||||
start: start,
|
||||
end: end,
|
||||
);
|
||||
|
||||
expect(
|
||||
mapper.fromTask(task).rewardIconToken,
|
||||
entry.value,
|
||||
reason: entry.key.name,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('difficulty icon token handles every difficulty level plus not set',
|
||||
() {
|
||||
final expectedTokens = {
|
||||
DifficultyLevel.notSet: TimelineDifficultyIconToken.notSet,
|
||||
DifficultyLevel.veryEasy: TimelineDifficultyIconToken.veryEasy,
|
||||
DifficultyLevel.easy: TimelineDifficultyIconToken.easy,
|
||||
DifficultyLevel.medium: TimelineDifficultyIconToken.medium,
|
||||
DifficultyLevel.hard: TimelineDifficultyIconToken.hard,
|
||||
DifficultyLevel.veryHard: TimelineDifficultyIconToken.veryHard,
|
||||
};
|
||||
|
||||
for (final entry in expectedTokens.entries) {
|
||||
final task = scheduledTask(
|
||||
id: entry.key.name,
|
||||
title: entry.key.name,
|
||||
projectId: project.id,
|
||||
type: TaskType.flexible,
|
||||
difficulty: entry.key,
|
||||
createdAt: createdAt,
|
||||
start: start,
|
||||
end: end,
|
||||
);
|
||||
|
||||
expect(
|
||||
mapper.fromTask(task).difficultyIconToken,
|
||||
entry.value,
|
||||
reason: entry.key.name,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('flexible task cards display duration while retaining position', () {
|
||||
final task = scheduledTask(
|
||||
id: 'laundry',
|
||||
|
|
@ -331,6 +421,64 @@ void main() {
|
|||
expect(state.nextRequiredItem!.id, 'required');
|
||||
expect(state.compactItems.map((item) => item.id), ['required']);
|
||||
});
|
||||
|
||||
test('compact mode returns only the reduced current and next items', () {
|
||||
final current = scheduledTask(
|
||||
id: 'current',
|
||||
title: 'Current',
|
||||
projectId: project.id,
|
||||
type: TaskType.flexible,
|
||||
createdAt: createdAt,
|
||||
start: DateTime(2026, 6, 20, 9),
|
||||
end: DateTime(2026, 6, 20, 10),
|
||||
);
|
||||
final nextRequired = scheduledTask(
|
||||
id: 'next-required',
|
||||
title: 'Next required',
|
||||
projectId: project.id,
|
||||
type: TaskType.inflexible,
|
||||
createdAt: createdAt,
|
||||
start: DateTime(2026, 6, 20, 10),
|
||||
end: DateTime(2026, 6, 20, 10, 30),
|
||||
);
|
||||
final laterRequired = scheduledTask(
|
||||
id: 'later-required',
|
||||
title: 'Later required',
|
||||
projectId: project.id,
|
||||
type: TaskType.critical,
|
||||
createdAt: createdAt,
|
||||
start: DateTime(2026, 6, 20, 12),
|
||||
end: DateTime(2026, 6, 20, 12, 30),
|
||||
);
|
||||
final laterFlexible = scheduledTask(
|
||||
id: 'later-flexible',
|
||||
title: 'Later flexible',
|
||||
projectId: project.id,
|
||||
type: TaskType.flexible,
|
||||
createdAt: createdAt,
|
||||
start: DateTime(2026, 6, 20, 13),
|
||||
end: DateTime(2026, 6, 20, 13, 30),
|
||||
);
|
||||
|
||||
final state = mapper.compactStateForTasks(
|
||||
tasks: [laterFlexible, laterRequired, nextRequired, current],
|
||||
now: DateTime(2026, 6, 20, 9, 15),
|
||||
manualCompactModeEnabled: true,
|
||||
);
|
||||
|
||||
expect(state.compactItems.map((item) => item.id), [
|
||||
'current',
|
||||
'next-required',
|
||||
]);
|
||||
expect(
|
||||
state.compactItems.map((item) => item.id),
|
||||
isNot(contains('later-required')),
|
||||
);
|
||||
expect(
|
||||
state.compactItems.map((item) => item.id),
|
||||
isNot(contains('later-flexible')),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue