diff --git a/Sources/XCTestHTMLReportCore/Classes/Models/Summary.swift b/Sources/XCTestHTMLReportCore/Classes/Models/Summary.swift
index 58831d5f..199a3efc 100644
--- a/Sources/XCTestHTMLReportCore/Classes/Models/Summary.swift
+++ b/Sources/XCTestHTMLReportCore/Classes/Models/Summary.swift
@@ -107,7 +107,9 @@ public struct Summary {
/// Call-site checks catch failures XCResultKit surfaces as `nil`. They do
/// not catch failures in *nested* decoding, where a parent object still
/// decodes but a child field comes back empty. The observable symptom is an
- /// attachment that resolved to no content, so check for that directly.
+ /// attachment whose referenced payload resolved to no content, so check
+ /// for that directly. Attachments without a payload reference legitimately
+ /// have no content and require no resolution.
///
/// Idempotent across sequential calls: repeated calls do not duplicate
/// faults. Dedup keys on `Attachment.faultDescription`, which assumes
@@ -123,7 +125,7 @@ public struct Summary {
)
for attachment in allAttachments {
- guard case .none = attachment.content else {
+ guard attachment.payloadId != nil, case .none = attachment.content else {
continue
}
let detail = attachment.faultDescription
diff --git a/Tests/XCTestHTMLReportTests/CoreTests.swift b/Tests/XCTestHTMLReportTests/CoreTests.swift
index 482f2890..f05caeb0 100644
--- a/Tests/XCTestHTMLReportTests/CoreTests.swift
+++ b/Tests/XCTestHTMLReportTests/CoreTests.swift
@@ -78,11 +78,11 @@ final class CoreTests: XCTestCase {
// because a run still depends on the simulator behaving, and an
// exact split would measure that rather than this project.
//
- // 17 = 14 XCTest methods + 3 Swift Testing `@Test` functions in
- // SwiftTestingSuite (SampleAppUnitTests target). The 14th is
- // FirstSuite.testAttachScreenshot, added by #393 to give the image
- // rendering path a fixture.
- XCTAssertEqual(all, 17, "One row per test method; fixed by the sample sources")
+ // 18 = 15 XCTest methods + 3 Swift Testing `@Test` functions in
+ // SwiftTestingSuite (SampleAppUnitTests target). The latest is
+ // FirstSuite.testAttachDeletedOnSuccess, whose passing attachment
+ // retains metadata but has no payload.
+ XCTAssertEqual(all, 18, "One row per test method; fixed by the sample sources")
XCTAssertEqual(
skipped,
1,
diff --git a/Tests/XCTestHTMLReportTests/FaultReportingTests.swift b/Tests/XCTestHTMLReportTests/FaultReportingTests.swift
index b7c6722b..8e918289 100644
--- a/Tests/XCTestHTMLReportTests/FaultReportingTests.swift
+++ b/Tests/XCTestHTMLReportTests/FaultReportingTests.swift
@@ -36,7 +36,7 @@ final class FaultReportingTests: XCTestCase {
XCTAssertEqual(summary.faults, [], "Clean fixture must not produce faults")
}
- func testValidateFlagsUnresolvedAttachments() throws {
+ func testValidateIgnoresPayloadlessAttachments() throws {
let url = try XCTUnwrap(
Bundle.testBundle.url(forResource: "TestResults", withExtension: "xcresult")
)
@@ -49,14 +49,27 @@ final class FaultReportingTests: XCTestCase {
downsizeScaleFactor: 0.25,
faultCollector: collector
)
+
+ let payloadlessAttachments = summary.allAttachments.filter { attachment in
+ guard attachment.payloadId == nil else {
+ return false
+ }
+ if case .none = attachment.content {
+ return true
+ }
+ return false
+ }
+ XCTAssertFalse(
+ payloadlessAttachments.isEmpty,
+ "Fixture must contain an attachment whose payload was deleted after a passing test"
+ )
+
summary.validate()
- // Every attachment the model knows about must have resolved to real
- // content. An unresolved one renders as an empty src.
let unresolved = summary.faults.filter { $0.kind == .unresolvedAttachment }
XCTAssertEqual(
unresolved, [],
- "Unresolved attachments: \(unresolved.map(\.detail))"
+ "Payload-less attachments must not be reported as unresolved: \(unresolved.map(\.detail))"
)
}
diff --git a/XCTestHTMLReportSampleApp/SampleAppUITests/FirstSuite.swift b/XCTestHTMLReportSampleApp/SampleAppUITests/FirstSuite.swift
index a472dc6b..35aabd62 100644
--- a/XCTestHTMLReportSampleApp/SampleAppUITests/FirstSuite.swift
+++ b/XCTestHTMLReportSampleApp/SampleAppUITests/FirstSuite.swift
@@ -65,6 +65,13 @@ class FirstSuite: XCTestCase {
add(screenshot)
}
+ func testAttachDeletedOnSuccess() {
+ let attachment = XCTAttachment(string: "This payload is removed after the test passes")
+ attachment.name = "Deleted on Success"
+ attachment.lifetime = .deleteOnSuccess
+ add(attachment)
+ }
+
func testOne() {
XCTContext.runActivity(named: "Text Attachment") { activity in
let logs = """