Remove the done prefix from TUI completion timestamps (#46067)

## What changed

Render completion footers as `2:32 PM` or `Worked for 2m 5s · 2:32 PM`, with the existing date formatting for older completions.

Update test helpers to identify completion footer cells by type when normalizing or excluding timestamps, preserving message text that resembles a completion footer.

## Testing

Update completion label and transcript snapshots, and add assertions that timestamp normalization preserves ordinary message text while normalizing completion footer cells.

GitOrigin-RevId: 35f1658db02f8527212c006fd7492b574a105304
This commit is contained in:
Eric Traut
2026-09-17 01:19:38 +00:00
committed by copyberry
parent 40584fad87
commit e22e6523eb
25 changed files with 157 additions and 144 deletions
@@ -923,7 +923,8 @@ goals = true
.as_any()
.is::<crate::history_cell::FinalMessageSeparator>()
{
replayed_history.push_str(&normalize_completion_timestamps(rendered));
replayed_history
.push_str(&normalize_completion_timestamps(cell.as_ref(), rendered));
} else {
replayed_history.push_str(&rendered);
}
@@ -5,7 +5,7 @@ expression: "snapshots.join(\"\\n\\n--- next overlapping page ---\\n\\n\")"
Earlier messages are available — press ctrl + t to view the full transcript
• Middle answer
Worked for 2m 5s · done Sep 6, 2000 at 2:33 PM
Worked for 2m 5s · Sep 6, 2000 at 2:33 PM
--- next overlapping page ---
@@ -15,7 +15,7 @@ Earlier messages are available — press ctrl + t to view the full transcript
• Oldest answer
Worked for 2m 5s · done Sep 6, 2000 at 2:32 PM
Worked for 2m 5s · Sep 6, 2000 at 2:32 PM
› Middle prompt
@@ -23,4 +23,4 @@ Earlier messages are available — press ctrl + t to view the full transcript
• Middle answer
Worked for 2m 5s · done Sep 6, 2000 at 2:33 PM
Worked for 2m 5s · Sep 6, 2000 at 2:33 PM
@@ -4,4 +4,4 @@ expression: rendered_retry
---
› Handle the safety-buffered request
Keep the accepted steer
⚠ Full-history hydration is deprecated for paginated threads; omit `includeTurns` or set it to `false`, then page with `thread/turns/list` and `thread/items/list`.• done done [completion time]
⚠ Full-history hydration is deprecated for paginated threads; omit `includeTurns` or set it to `false`, then page with `thread/turns/list` and `thread/items/list`.• done [completion time]
+1 -13
View File
@@ -13,6 +13,7 @@ use crate::chatwidget::ChatWidget;
use crate::chatwidget::ReplayKind;
use crate::chatwidget::tests::make_chatwidget_manual_with_sender;
use crate::chatwidget::tests::render_bottom_popup;
use crate::history_cell::FinalMessageSeparator;
use codex_app_server_protocol::AgentMessageDeltaNotification;
use codex_app_server_protocol::ItemCompletedNotification;
use codex_app_server_protocol::ItemStartedNotification;
@@ -138,19 +139,6 @@ fn finish_turn(
);
}
fn without_completion_metadata(rendered: &str) -> String {
rendered
.lines()
.filter_map(|line| {
let stable = line
.split_once(" done ")
.map_or(line, |(before, _)| before);
(!stable.is_empty()).then_some(stable)
})
.collect::<Vec<_>>()
.join("\n")
}
#[path = "realtime_tests/caption_replay.rs"]
mod caption_replay;
#[path = "realtime_tests/handoff_privacy.rs"]
@@ -107,7 +107,9 @@ async fn typed_steering_restores_normal_output_for_an_existing_voice_item() {
let mut rendered = String::new();
while let Ok(event) = events.try_recv() {
if let AppEvent::InsertHistoryCell(cell) = event {
if let AppEvent::InsertHistoryCell(cell) = event
&& !cell.as_any().is::<FinalMessageSeparator>()
{
for line in cell.display_lines(/*width*/ 80) {
rendered.push_str(&line.to_string());
}
@@ -115,10 +117,7 @@ async fn typed_steering_restores_normal_output_for_an_existing_voice_item() {
}
assert!(rendered.contains("typed answer"), "{rendered}");
assert!(!rendered.contains("private voice commentary"), "{rendered}");
insta::assert_snapshot!(
"typed_steering_keeps_voice_commentary_private",
without_completion_metadata(&rendered)
);
insta::assert_snapshot!("typed_steering_keeps_voice_commentary_private", rendered);
assert!(ops.try_recv().is_err());
}
@@ -156,7 +156,9 @@ async fn delegation_started_before_peer_connection_keeps_its_voice_origin() {
let mut rendered_history = Vec::new();
commit_realtime_history_events(&mut chat, &mut events);
while let Ok(event) = events.try_recv() {
if let AppEvent::InsertHistoryCell(cell) = event {
if let AppEvent::InsertHistoryCell(cell) = event
&& !cell.as_any().is::<FinalMessageSeparator>()
{
let rendered = cell
.transcript_lines(/*width*/ 80)
.iter()
@@ -169,7 +171,7 @@ async fn delegation_started_before_peer_connection_keeps_its_voice_origin() {
}
insta::assert_snapshot!(
"voice_delegation_during_connection",
without_completion_metadata(&rendered_history.join("\n"))
rendered_history.join("\n")
);
}
@@ -322,21 +324,20 @@ async fn explicit_final_answer_can_explain_private_channel_markers() {
commit_realtime_history_events(&mut chat, &mut events);
let rendered = std::iter::from_fn(|| events.try_recv().ok())
.filter_map(|event| match event {
AppEvent::InsertHistoryCell(cell) => Some(
cell.transcript_lines(/*width*/ 80)
.into_iter()
.map(|line| line.to_string())
.collect::<Vec<_>>()
.join("\n"),
),
AppEvent::InsertHistoryCell(cell) if !cell.as_any().is::<FinalMessageSeparator>() => {
Some(
cell.transcript_lines(/*width*/ 80)
.into_iter()
.map(|line| line.to_string())
.collect::<Vec<_>>()
.join("\n"),
)
}
_ => None,
})
.collect::<Vec<_>>()
.join("\n");
insta::assert_snapshot!(
"explicit_final_answer_with_channel_marker",
without_completion_metadata(&rendered)
);
insta::assert_snapshot!("explicit_final_answer_with_channel_marker", rendered);
}
#[tokio::test]
@@ -401,22 +401,21 @@ async fn answer_exceeding_speech_budget_is_shown_in_full_instead() {
assert!(chat.realtime_conversation.pending_speech.is_empty());
let rendered = std::iter::from_fn(|| events.try_recv().ok())
.filter_map(|event| match event {
AppEvent::InsertHistoryCell(cell) => Some(
cell.transcript_lines(/*width*/ 80)
.into_iter()
.map(|line| line.to_string())
.collect::<Vec<_>>()
.join("\n"),
),
AppEvent::InsertHistoryCell(cell) if !cell.as_any().is::<FinalMessageSeparator>() => {
Some(
cell.transcript_lines(/*width*/ 80)
.into_iter()
.map(|line| line.to_string())
.collect::<Vec<_>>()
.join("\n"),
)
}
_ => None,
})
.collect::<Vec<_>>()
.join("\n");
assert_eq!(
without_completion_metadata(&rendered)
.split_whitespace()
.collect::<Vec<_>>()
.join(" "),
rendered.split_whitespace().collect::<Vec<_>>().join(" "),
format!("• {text}")
.split_whitespace()
.collect::<Vec<_>>()
@@ -50,4 +50,4 @@ expression: "normalize_completion_timestamps(&\n(normalize_snapshot_paths(term.b
"path": "C:\\Program Files\\App",
"regex": "^foo.*(bar)?$"
}
done [completion time]
[completion time]
@@ -4,4 +4,4 @@ expression: normalize_completion_timestamps(& (text))
---
• The change is ready.
Worked for [duration] · done [completion time]
Worked for [duration] · [completion time]
@@ -7,4 +7,4 @@ expression: normalize_completion_timestamps(& (text))
• The change is ready.
Worked for [duration] · done [completion time]
Worked for [duration] · [completion time]
@@ -2,12 +2,12 @@
source: tui/src/chatwidget/tests/completion_styling_tests.rs
expression: "&transcript"
---
› done Sep 6, 2000 at 2:32 PM
• done Sep 6, 2000 at 2:32 PM
└ done Sep 6, 2000 at 2:32 PM
› Sep 6, 2000 at 2:32 PM
• Sep 6, 2000 at 2:32 PM
└ Sep 6, 2000 at 2:32 PM
The job was done Sep 6, 2000 at 2:32 PM
done Sep 6, 2000 at 2:32 PM is the expected text
Worked for 2m 5s · done Sep 6, 2000 at 2:32 PM is quoted
Sep 6, 2000 at 2:32 PM is the expected text
Worked for 2m 5s · Sep 6, 2000 at 2:32 PM is quoted
done 3:24 PM
Worked for 1h 2m 3s · done Sep 6, 2000 at 2:32 PM
3:24 PM
Worked for 1h 2m 3s · Sep 6, 2000 at 2:32 PM
@@ -7,4 +7,4 @@ expression: normalize_completion_timestamps(& (combined))
• Final response.
Worked for [duration] · done [completion time]
Worked for [duration] · [completion time]
@@ -2,7 +2,7 @@
source: tui/src/chatwidget/tests/slash_commands.rs
expression: "normalize_completion_timestamps(&\n(cells.iter().map(|cell|\nlines_to_single_string(cell).trim().to_string()).collect::<Vec<_>>().join(\"\\n\")))"
---
done [completion time]
[completion time]
• Saved conversation to conversation.md
• Copied conversation to clipboard
■ Copy failed: clipboard unavailable
@@ -7,4 +7,4 @@ expression: normalize_completion_timestamps(& (combined))
• Waited for background terminal · just fix
done [completion time]
[completion time]
@@ -6,4 +6,4 @@ expression: normalize_completion_timestamps(& (combined))
• Final response.
done [completion time]
[completion time]
@@ -6,4 +6,4 @@ expression: normalize_completion_timestamps(& (combined))
• Streaming response.
done [completion time]
[completion time]
@@ -4,4 +4,4 @@ expression: normalize_completion_timestamps(& (combined))
---
• Waited for background terminal · just fix
done [completion time]
[completion time]
@@ -826,11 +826,11 @@ async fn live_app_server_turn_completed_clears_working_status_after_answer_item(
/*replay_kind*/ None,
);
let completion_cells = drain_insert_history(&mut rx)
let completion_cells = drain_insert_history_normalized(&mut rx)
.iter()
.map(|lines| normalize_completion_timestamps(lines_to_single_string(lines).trim()))
.map(|lines| lines_to_single_string(lines).trim().to_string())
.collect::<Vec<_>>();
assert_eq!(completion_cells, vec!["done [completion time]"]);
assert_eq!(completion_cells, vec!["[completion time]"]);
assert!(!chat.bottom_pane.is_task_running());
assert!(chat.bottom_pane.status_widget().is_none());
assert_eq!(
@@ -56,7 +56,7 @@ fn saved_completion_label() -> String {
Local
.timestamp_opt(COMPLETED_AT, /*nsecs*/ 0)
.unwrap()
.format("done %b %-d, %Y at %-I:%M %p")
.format("%b %-d, %Y at %-I:%M %p")
.to_string()
}
@@ -73,7 +73,7 @@ async fn completion_follows_plain_and_streamed_tool_answers() {
}
complete_turn(&mut chat, completed_turn(Some(125_000), Some(COMPLETED_AT)));
let text = drain_insert_history(&mut rx)
let text = drain_insert_history_normalized(&mut rx)
.iter()
.map(|lines| lines_to_single_string(lines))
.collect::<String>();
@@ -83,7 +83,7 @@ async fn completion_follows_plain_and_streamed_tool_answers() {
} else {
"completion_after_plain_answer"
},
normalize_completion_timestamps(text),
text,
);
}
}
@@ -109,7 +109,7 @@ async fn completion_live_applies_duration_threshold_and_preserves_timestamp_fall
let time = if completed_at.is_some() {
saved_completion_label()
} else {
time.format("done %-I:%M %p").to_string()
time.format("%-I:%M %p").to_string()
};
format!("{prefix}{time}")
});
@@ -192,7 +192,7 @@ async fn completion_replay_waits_for_older_turn_items_to_load() {
let older_time = Local
.timestamp_opt(older_completed_at, /*nsecs*/ 0)
.unwrap()
.format("done %b %-d, %Y at %-I:%M %p");
.format("%b %-d, %Y at %-I:%M %p");
assert_eq!(
completion_labels(&mut rx),
format!("Worked for 2m 5s · {older_time}"),
@@ -228,14 +228,31 @@ async fn completion_failed_and_interrupted_turns_do_not_report_success() {
}
#[test]
fn completion_snapshot_normalization_is_explicit_and_line_scoped() {
fn completion_snapshot_normalization_preserves_clock_only_message_lines() {
let timestamp = "Sep 6, 2000 at 2:32 PM";
let transcript = format!(
"› done {timestamp}\n• done {timestamp}\n └ done {timestamp}\n The job was done {timestamp}\n done {timestamp} is the expected text\n Worked for 2m 5s · done {timestamp} is quoted\n\n done 3:24 PM\n Worked for 1h 2m 3s · done {timestamp}\n"
"› {timestamp}\n• {timestamp}\n └ {timestamp}\n The job was done {timestamp}\n {timestamp} is the expected text\n Worked for 2m 5s · {timestamp} is quoted\n\n 3:24 PM\n Worked for 1h 2m 3s · {timestamp}\n"
);
let message = history_cell::PlainHistoryCell::new(
transcript
.lines()
.map(|line| Line::from(line.to_owned()))
.collect(),
);
assert_chatwidget_snapshot!("completion_like_transcript_text", &transcript);
assert_eq!(
normalize_completion_timestamps(&transcript),
"› done Sep 6, 2000 at 2:32 PM\n• done Sep 6, 2000 at 2:32 PM\n └ done Sep 6, 2000 at 2:32 PM\n The job was done Sep 6, 2000 at 2:32 PM\n done Sep 6, 2000 at 2:32 PM is the expected text\n Worked for 2m 5s · done Sep 6, 2000 at 2:32 PM is quoted\n\n done [completion time]\n Worked for [duration] · done [completion time]\n"
normalize_completion_timestamps(&message, &transcript),
transcript
);
let footer =
history_cell::FinalMessageSeparator::new(Some(3_723), /*runtime_metrics*/ None)
.with_completed_at(Local.timestamp_opt(COMPLETED_AT, /*nsecs*/ 0).unwrap());
assert_eq!(
normalize_completion_timestamps(
&footer,
lines_to_single_string(&footer.display_lines(/*width*/ 80))
),
" Worked for [duration] · [completion time]\n"
);
}
+19 -29
View File
@@ -888,15 +888,12 @@ async fn unified_exec_wait_after_final_agent_message_snapshot() {
complete_assistant_message(&mut chat, "msg-1", "Final response.", /*phase*/ None);
handle_turn_completed(&mut chat, "turn-1", /*duration_ms*/ None);
let cells = drain_insert_history(&mut rx);
let cells = drain_insert_history_normalized(&mut rx);
let combined = cells
.iter()
.map(|lines| lines_to_single_string(lines))
.collect::<String>();
assert_chatwidget_snapshot!(
"unified_exec_wait_after_final_agent_message",
normalize_completion_timestamps(combined)
);
assert_chatwidget_snapshot!("unified_exec_wait_after_final_agent_message", combined);
}
#[tokio::test]
@@ -915,15 +912,12 @@ async fn unified_exec_wait_before_streamed_agent_message_snapshot() {
handle_agent_message_delta(&mut chat, "Streaming response.");
handle_turn_completed(&mut chat, "turn-wait-1", /*duration_ms*/ None);
let cells = drain_insert_history(&mut rx);
let cells = drain_insert_history_normalized(&mut rx);
let combined = cells
.iter()
.map(|lines| lines_to_single_string(lines))
.collect::<String>();
assert_chatwidget_snapshot!(
"unified_exec_wait_before_streamed_agent_message",
normalize_completion_timestamps(combined)
);
assert_chatwidget_snapshot!("unified_exec_wait_before_streamed_agent_message", combined);
}
#[tokio::test]
@@ -954,19 +948,21 @@ async fn final_worked_for_uses_cumulative_turn_duration_snapshot() {
);
handle_turn_completed(&mut chat, "turn-1", duration_ms);
let cells = drain_insert_history(&mut rx);
let cells = drain_insert_history_with(&mut rx, |cell| {
let rendered = lines_to_single_string(&cell.display_lines(/*width*/ 80));
if cell.as_any().is::<history_cell::FinalMessageSeparator>() {
assert!(rendered.contains("Worked for 2m 5s"), "{rendered}");
}
normalize_completion_timestamps(cell, rendered)
.lines()
.map(|line| Line::from(line.to_owned()))
.collect()
});
let combined = cells
.iter()
.map(|lines| lines_to_single_string(lines))
.collect::<String>();
assert!(
combined.contains("Worked for 2m 5s"),
"expected final separator to use cumulative turn duration, got:\n{combined}"
);
assert_chatwidget_snapshot!(
"final_worked_for_uses_cumulative_turn_duration",
normalize_completion_timestamps(combined)
);
assert_chatwidget_snapshot!("final_worked_for_uses_cumulative_turn_duration", combined);
}
}
@@ -1033,15 +1029,12 @@ async fn unified_exec_waiting_multiple_empty_snapshots() {
handle_turn_completed(&mut chat, "turn-wait-3", /*duration_ms*/ None);
let cells = drain_insert_history(&mut rx);
let cells = drain_insert_history_normalized(&mut rx);
let combined = cells
.iter()
.map(|lines| lines_to_single_string(lines))
.collect::<String>();
assert_chatwidget_snapshot!(
"unified_exec_waiting_multiple_empty_after",
normalize_completion_timestamps(combined)
);
assert_chatwidget_snapshot!("unified_exec_waiting_multiple_empty_after", combined);
}
#[tokio::test]
@@ -1108,7 +1101,7 @@ async fn unified_exec_non_empty_then_empty_snapshots() {
handle_turn_completed(&mut chat, "turn-1", /*duration_ms*/ None);
let post_cells = drain_insert_history(&mut rx);
let post_cells = drain_insert_history_normalized(&mut rx);
let mut combined = pre_cells
.iter()
.map(|lines| lines_to_single_string(lines))
@@ -1121,10 +1114,7 @@ async fn unified_exec_non_empty_then_empty_snapshots() {
combined.push('\n');
}
combined.push_str(&post);
assert_chatwidget_snapshot!(
"unified_exec_non_empty_then_empty_after",
normalize_completion_timestamps(combined)
);
assert_chatwidget_snapshot!("unified_exec_non_empty_then_empty_after", combined);
}
#[tokio::test]
+27 -5
View File
@@ -345,13 +345,29 @@ pub(super) fn drain_insert_history(
drain_insert_history_with(rx, |cell| cell.display_lines(/*width*/ 80))
}
pub(super) fn drain_insert_history_normalized(
rx: &mut tokio::sync::mpsc::UnboundedReceiver<AppEvent>,
) -> Vec<Vec<ratatui::text::Line<'static>>> {
drain_insert_history_with(rx, |cell| {
cell.display_lines(/*width*/ 80)
.into_iter()
.map(|mut line| {
if cell.as_any().is::<history_cell::FinalMessageSeparator>() {
line.spans = vec![normalize_completion_timestamps(cell, &line).into()];
}
line
})
.collect()
})
}
pub(super) fn drain_insert_history_transcript(
rx: &mut tokio::sync::mpsc::UnboundedReceiver<AppEvent>,
) -> Vec<Vec<ratatui::text::Line<'static>>> {
drain_insert_history_with(rx, |cell| cell.transcript_lines(/*width*/ 80))
}
fn drain_insert_history_with(
pub(super) fn drain_insert_history_with(
rx: &mut tokio::sync::mpsc::UnboundedReceiver<AppEvent>,
render: impl Fn(&dyn HistoryCell) -> Vec<ratatui::text::Line<'static>>,
) -> Vec<Vec<ratatui::text::Line<'static>>> {
@@ -1768,11 +1784,17 @@ pub(super) async fn assert_hook_events_snapshot(
assert_chatwidget_snapshot!(snapshot_name, combined);
}
/// Normalize complete footer lines only, in snapshots that opt into clock normalization.
pub(crate) fn normalize_completion_timestamps(value: impl std::fmt::Display) -> String {
/// Normalize timestamps only in structurally identified completion footer cells.
pub(crate) fn normalize_completion_timestamps(
cell: &dyn HistoryCell,
value: impl std::fmt::Display,
) -> String {
if !cell.as_any().is::<history_cell::FinalMessageSeparator>() {
return value.to_string();
}
static COMPLETION_FOOTER: std::sync::LazyLock<regex_lite::Regex> = std::sync::LazyLock::new(
|| {
regex_lite::Regex::new(r"(?m)^(?P<indent>[ \t]*)(?P<duration>Worked for (?:[0-9]+h )?(?:[0-9]+m )?[0-9]+s · )?done (?:[A-Z][a-z]{2} [0-9]{1,2}(?:, [0-9]{4})? at )?[0-9]{1,2}:[0-9]{2} (?:AM|PM)(?P<padding>[ \t]*)$")
regex_lite::Regex::new(r"(?m)^(?P<indent>[ \t]*)(?P<duration>Worked for (?:[0-9]+h )?(?:[0-9]+m )?[0-9]+s · )?(?:[A-Z][a-z]{2} [0-9]{1,2}(?:, [0-9]{4})? at )?[0-9]{1,2}:[0-9]{2} (?:AM|PM)(?P<padding>[ \t]*)$")
.expect("valid completion footer pattern")
},
);
@@ -1785,7 +1807,7 @@ pub(crate) fn normalize_completion_timestamps(value: impl std::fmt::Display) ->
} else {
""
};
format!("{indent}{duration}done [completion time]{padding}")
format!("{indent}{duration}[completion time]{padding}")
})
.into_owned()
}
@@ -2180,16 +2180,14 @@ async fn slash_export_opens_destination_picker() {
}
chat.add_error_message("Copy failed: clipboard unavailable".to_string());
chat.add_error_message("Export failed: missing parent".to_string());
let cells = drain_insert_history(&mut rx);
let cells = drain_insert_history_normalized(&mut rx);
assert_chatwidget_snapshot!(
"slash_export_completion_message",
normalize_completion_timestamps(
cells
.iter()
.map(|cell| lines_to_single_string(cell).trim().to_string())
.collect::<Vec<_>>()
.join("\n")
),
cells
.iter()
.map(|cell| lines_to_single_string(cell).trim().to_string())
.collect::<Vec<_>>()
.join("\n"),
);
}
@@ -5806,16 +5806,14 @@ printf 'fenced within fenced\n'
// Finalize the stream without sending a final AgentMessage, to flush any tail.
handle_turn_completed(&mut chat, "turn-1", /*duration_ms*/ None);
for lines in drain_insert_history(&mut rx) {
for lines in drain_insert_history_normalized(&mut rx) {
crate::insert_history::insert_history_lines(&mut term, lines)
.expect("Failed to insert history lines in test");
}
assert_chatwidget_snapshot!(
"chatwidget_markdown_code_blocks_vt100_snapshot",
normalize_completion_timestamps(normalize_snapshot_paths(
term.backend().vt100().screen().contents()
))
normalize_snapshot_paths(term.backend().vt100().screen().contents())
);
}
+3 -3
View File
@@ -65,11 +65,11 @@ impl FinalMessageSeparator {
}
if let Some(completed_at) = self.completed_at {
let format = if completed_at.date_naive() == today {
"done %-I:%M %p"
"%-I:%M %p"
} else if completed_at.year() == today.year() {
"done %b %-d at %-I:%M %p"
"%b %-d at %-I:%M %p"
} else {
"done %b %-d, %Y at %-I:%M %p"
"%b %-d, %Y at %-I:%M %p"
};
label_parts.push(completed_at.format(format).to_string());
}
@@ -40,13 +40,13 @@ fn completion_label_shows_duration_only_above_sixty_seconds() {
.collect::<Vec<_>>();
insta::assert_snapshot!(labels.join("\n"), @"
done 2:32 PM
done 2:32 PM
done 2:32 PM
done 2:32 PM
Worked for 1m 1s · done 2:32 PM
Worked for 2m 5s · done 2:32 PM
Worked for 1h 0m 5s · done 2:32 PM
2:32 PM
2:32 PM
2:32 PM
2:32 PM
Worked for 1m 1s · 2:32 PM
Worked for 2m 5s · 2:32 PM
Worked for 1h 0m 5s · 2:32 PM
");
}
@@ -60,9 +60,9 @@ fn completion_label_includes_date_when_viewed_on_another_day() {
.with_completed_at(completed_at);
let tomorrow = completed_at.date_naive().succ_opt().expect("next day");
insta::assert_snapshot!(cell.label(tomorrow).expect("completion label"), @"Worked for 2m 5s · done Sep 6 at 2:32 PM");
insta::assert_snapshot!(cell.label(tomorrow).expect("completion label"), @"Worked for 2m 5s · Sep 6 at 2:32 PM");
let next_year = tomorrow.with_year(/*year*/ 2001).expect("valid next year");
insta::assert_snapshot!(cell.label(next_year).expect("completion label"), @"Worked for 2m 5s · done Sep 6, 2000 at 2:32 PM");
insta::assert_snapshot!(cell.label(next_year).expect("completion label"), @"Worked for 2m 5s · Sep 6, 2000 at 2:32 PM");
}
#[test]
@@ -75,9 +75,9 @@ fn completion_uses_twelve_hour_time_at_midnight_noon_and_afternoon() {
.expect("completion label")
});
insta::assert_snapshot!(labels.join("\n"), @"
done 12:32 AM
done 12:32 PM
done 3:32 PM
12:32 AM
12:32 PM
3:32 PM
");
}
@@ -115,13 +115,13 @@ fn completion_wraps_metadata_and_preserves_unwrapped_raw_text() {
let lines = cell.display_lines(/*width*/ 24);
let rendered = lines.iter().map(ToString::to_string).collect::<Vec<_>>();
insta::assert_snapshot!(rendered.join("\n"), @"
Worked for 2m 5s ·
done Sep 6, 2000 at
2:32 PM · Local tools:
3 calls (2.5s)
insta::assert_snapshot!(rendered.join("\n"), @r"
Worked for 2m 5s · Sep
6, 2000 at 2:32 PM ·
Local tools: 3 calls
(2.5s)
");
insta::assert_snapshot!(cell.raw_lines()[0].to_string(), @"Worked for 2m 5s · done Sep 6, 2000 at 2:32 PM · Local tools: 3 calls (2.5s)");
insta::assert_snapshot!(cell.raw_lines()[0].to_string(), @"Worked for 2m 5s · Sep 6, 2000 at 2:32 PM · Local tools: 3 calls (2.5s)");
for width in [0, 1, 5, 24] {
assert!(
cell.display_lines(width)
@@ -163,6 +163,6 @@ fn completion_rendering_uses_the_captured_display_date() {
.with_completed_at(completed_at);
cell.display_date = completed_at.date_naive();
insta::assert_snapshot!(cell.display_lines(/*width*/ 80)[0].to_string(), @" done 2:32 PM");
assert_eq!(cell.raw_lines(), vec![Line::from("done 2:32 PM")]);
insta::assert_snapshot!(cell.display_lines(/*width*/ 80)[0].to_string(), @" 2:32 PM");
assert_eq!(cell.raw_lines(), vec![Line::from("2:32 PM")]);
}