doc: Document plain clear-on finish behavior (#1379)

- Clarified the `.plain` progress contract. This also
  documents how `clearOnFinish` interacts with plain
  output. Added test coverage for the current behavior.
This commit is contained in:
Manoj Mahapatra
2026-04-02 13:05:52 -07:00
committed by GitHub
parent 220fc0687f
commit ce77361057
2 changed files with 26 additions and 4 deletions
@@ -62,6 +62,7 @@ public struct ProgressConfig: Sendable {
/// The theme of the progress bar.
public let theme: ProgressTheme
/// The flag indicating whether to clear the progress bar before resetting the cursor.
/// In `.plain` mode, `finish()` only emits a final line when this value is `false`.
public let clearOnFinish: Bool
/// The flag indicating whether to update the progress bar.
public let disableProgressUpdates: Bool
@@ -89,7 +90,8 @@ public struct ProgressConfig: Sendable {
/// - totalSize: The initial total size of the progress bar. The default value is `nil`.
/// - width: The width of the progress bar in characters. The default value is `120`.
/// - theme: The theme of the progress bar. The default value is `nil`.
/// - clearOnFinish: The flag indicating whether to clear the progress bar before resetting the cursor. The default is `true`.
/// - clearOnFinish: The flag indicating whether to clear the progress bar before resetting the cursor. In `.plain` mode,
/// `finish()` only emits a final line when this value is `false`. The default is `true`.
/// - disableProgressUpdates: The flag indicating whether to update the progress bar. The default is `false`.
/// - outputMode: The output mode for progress rendering. The default is `.ansi`.
public init(
@@ -880,7 +880,7 @@ final class ProgressBarTests: XCTestCase {
try pipe.fileHandleForWriting.close()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8) ?? ""
let output = String(decoding: data, as: UTF8.self)
let lines = output.components(separatedBy: "\n").filter { !$0.isEmpty }
// Expect exactly 2 lines: one from render, one from finish
XCTAssertEqual(lines.count, 2)
@@ -903,7 +903,7 @@ final class ProgressBarTests: XCTestCase {
try pipe.fileHandleForWriting.close()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8) ?? ""
let output = String(decoding: data, as: UTF8.self)
XCTAssertFalse(output.contains("\u{001B}"))
}
@@ -922,12 +922,32 @@ final class ProgressBarTests: XCTestCase {
try pipe.fileHandleForWriting.close()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8) ?? ""
let output = String(decoding: data, as: UTF8.self)
// Plain mode should use newlines, not carriage returns
XCTAssertFalse(output.contains("\r"))
XCTAssertTrue(output.contains("\n"))
}
func testPlainModeDefaultClearOnFinishOmitsFinalLine() async throws {
let pipe = Pipe()
let config = try ProgressConfig(
terminal: pipe.fileHandleForWriting,
description: "Task",
showSpinner: false,
outputMode: .plain
)
let progress = ProgressBar(config: config)
progress.render(force: true)
progress.finish()
try pipe.fileHandleForWriting.close()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(decoding: data, as: UTF8.self)
let lines = output.components(separatedBy: "\n").filter { !$0.isEmpty }
XCTAssertEqual(lines.count, 1)
XCTAssertEqual(lines.first, "Task [0s]")
}
func testOutputModeDefaultIsAnsi() async throws {
let config = try ProgressConfig(description: "Task")
XCTAssertEqual(config.outputMode, .ansi)