Prompt for a meeting name on stop; rename the session folder
When a recording finishes, ask for a meeting name and rename the session folder from the auto stamp `<yyyy-MM-dd'T'HH-mm-ss>_<app>` to the readable `<date>_<name>_<app>` (dropping HH-MM-SS), so sessions/ is easy to scan. Skipping or leaving it blank keeps the timestamped name. The rename runs after the recorder and visual capture finish (files closed) and before finish() captures the folder for backend processing, so the renamed folder is what flows downstream; finish() re-derives the track URLs from the possibly-moved folder. The quit path never prompts, and a quit with the prompt open ends its modal so termination isn't blocked. Naming/parsing logic lives in a pure, unit-tested SessionNaming; recapTitle moves there and now understands both folder forms.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import XCTest
|
||||
@testable import Ten31Transcripts
|
||||
|
||||
final class SessionNamingTests: XCTestCase {
|
||||
|
||||
// MARK: sanitize
|
||||
|
||||
func testSanitizeTrimsAndKeepsSpaces() {
|
||||
XCTAssertEqual(SessionNaming.sanitize(" Weekly Sync "), "Weekly Sync")
|
||||
}
|
||||
|
||||
func testSanitizeReplacesPathSeparators() {
|
||||
XCTAssertEqual(SessionNaming.sanitize("9/10 standup"), "9-10 standup")
|
||||
XCTAssertEqual(SessionNaming.sanitize("a:b\\c"), "a-b-c")
|
||||
}
|
||||
|
||||
func testSanitizeCollapsesWhitespaceRuns() {
|
||||
XCTAssertEqual(SessionNaming.sanitize("board 1:1"), "board 1-1")
|
||||
}
|
||||
|
||||
func testSanitizeStripsLeadingDots() {
|
||||
XCTAssertEqual(SessionNaming.sanitize("...hidden"), "hidden")
|
||||
XCTAssertEqual(SessionNaming.sanitize(".."), "")
|
||||
}
|
||||
|
||||
func testSanitizeEmptyForBlankOrWhitespace() {
|
||||
XCTAssertEqual(SessionNaming.sanitize(""), "")
|
||||
XCTAssertEqual(SessionNaming.sanitize(" \n\t "), "")
|
||||
}
|
||||
|
||||
func testSanitizeCapsLength() {
|
||||
let long = String(repeating: "x", count: 200)
|
||||
XCTAssertEqual(SessionNaming.sanitize(long).count, 60)
|
||||
}
|
||||
|
||||
func testSanitizeStripsControlCharacters() {
|
||||
XCTAssertEqual(SessionNaming.sanitize("a\u{0000}b\u{001F}c"), "abc")
|
||||
}
|
||||
|
||||
// MARK: datePrefix
|
||||
|
||||
func testDatePrefixFromAutoName() {
|
||||
XCTAssertEqual(SessionNaming.datePrefix(ofSessionNamed: "2026-06-17T09-59-48_signal"), "2026-06-17")
|
||||
}
|
||||
|
||||
func testDatePrefixFromRenamedName() {
|
||||
XCTAssertEqual(SessionNaming.datePrefix(ofSessionNamed: "2026-06-17_Weekly sync_signal"), "2026-06-17")
|
||||
}
|
||||
|
||||
// MARK: renamedLeaf
|
||||
|
||||
func testRenamedLeafBasic() {
|
||||
XCTAssertEqual(
|
||||
SessionNaming.renamedLeaf(date: "2026-06-17", app: "signal", meetingName: "Weekly sync"),
|
||||
"2026-06-17_Weekly sync_signal")
|
||||
}
|
||||
|
||||
func testRenamedLeafAppStaysLastSegment() {
|
||||
// The meeting name may contain underscores; the app must remain parseable as
|
||||
// the final "_"-segment (what SessionController.appLabel reads).
|
||||
let leaf = SessionNaming.renamedLeaf(date: "2026-06-17", app: "meet", meetingName: "q3_planning")
|
||||
XCTAssertEqual(leaf, "2026-06-17_q3_planning_meet")
|
||||
XCTAssertEqual(leaf?.split(separator: "_").last.map(String.init), "meet")
|
||||
}
|
||||
|
||||
func testRenamedLeafNilForBlankName() {
|
||||
XCTAssertNil(SessionNaming.renamedLeaf(date: "2026-06-17", app: "signal", meetingName: " "))
|
||||
}
|
||||
|
||||
func testRenamedLeafCounterDisambiguatesNameSegment() {
|
||||
// A collision suffixes the NAME, not the whole leaf, so "_app" stays last.
|
||||
let leaf = SessionNaming.renamedLeaf(date: "2026-06-17", app: "signal", meetingName: "sync", counter: 1)
|
||||
XCTAssertEqual(leaf, "2026-06-17_sync-2_signal")
|
||||
XCTAssertEqual(leaf?.split(separator: "_").last.map(String.init), "signal")
|
||||
}
|
||||
|
||||
func testRenamedLeafAppStaysLastAtMaxCollisionDepth() {
|
||||
// The 100-collision cap is counter 0…99; the app must still parse out last.
|
||||
let leaf = SessionNaming.renamedLeaf(date: "2026-06-17", app: "signal", meetingName: "q3_sync", counter: 99)
|
||||
XCTAssertEqual(leaf, "2026-06-17_q3_sync-100_signal")
|
||||
XCTAssertEqual(leaf?.split(separator: "_").last.map(String.init), "signal")
|
||||
}
|
||||
|
||||
// MARK: recapTitle
|
||||
|
||||
func testRecapTitleAutoNamePreservesLegacyFormat() {
|
||||
XCTAssertEqual(
|
||||
SessionNaming.recapTitle(app: "meet", sessionId: "2026-06-06T11-43-02_meet"),
|
||||
"Google Meet call — 2026-06-06 11:43")
|
||||
}
|
||||
|
||||
func testRecapTitleNamedSession() {
|
||||
XCTAssertEqual(
|
||||
SessionNaming.recapTitle(app: "meet", sessionId: "2026-06-06_Weekly sync_meet"),
|
||||
"Weekly sync — Google Meet (2026-06-06)")
|
||||
}
|
||||
|
||||
func testRecapTitleNamePreservesUnderscores() {
|
||||
// A meeting name with underscores must survive the split/join round-trip.
|
||||
XCTAssertEqual(
|
||||
SessionNaming.recapTitle(app: "meet", sessionId: "2026-06-06_q3_planning_meet"),
|
||||
"q3_planning — Google Meet (2026-06-06)")
|
||||
}
|
||||
|
||||
func testRecapTitleUnknownAppCapitalizes() {
|
||||
XCTAssertEqual(
|
||||
SessionNaming.recapTitle(app: "manual", sessionId: "2026-06-06T11-43-02_manual"),
|
||||
"Manual call — 2026-06-06 11:43")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user