From af5ef808ae325961165bd2dc504002308d8e9a53 Mon Sep 17 00:00:00 2001 From: Marie Denis Date: Mon, 1 Jul 2024 17:48:33 +0200 Subject: [PATCH 1/6] RUM-4150 [SR] Add Activity Indicator recorder --- .../Sources/SRFixtures/Fixtures.swift | 3 + .../Storyboards/InputElements.storyboard | 114 ++++++++++++++++-- .../Storyboards/UnsupportedViews.storyboard | 19 +-- .../SRHost/MenuViewController.swift | 2 + .../SRSnapshotTests/SRSnapshotTests.swift | 13 ++ .../UIActivityIndicatorRecorder.swift | 71 +++++++++++ .../UnsupportedViewRecorder.swift | 3 +- .../ViewTreeSnapshotBuilder.swift | 3 +- .../UIActivityIndicatorRecorderTests.swift | 74 ++++++++++++ .../UnsupportedViewRecorderTests.swift | 3 +- 10 files changed, 277 insertions(+), 28 deletions(-) create mode 100644 DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorder.swift create mode 100644 DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorderTests.swift diff --git a/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Fixtures.swift b/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Fixtures.swift index 6babea37b3..1438276ea0 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Fixtures.swift +++ b/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Fixtures.swift @@ -12,6 +12,7 @@ public enum Fixture: CaseIterable { case basicTexts case sliders case progressViews + case activityIndicators case segments case pickers case switches @@ -57,6 +58,8 @@ public enum Fixture: CaseIterable { return UIStoryboard.inputElements.instantiateViewController(withIdentifier: "Sliders") case .progressViews: return UIStoryboard.inputElements.instantiateViewController(withIdentifier: "ProgressViews") + case .activityIndicators: + return UIStoryboard.inputElements.instantiateViewController(withIdentifier: "ActivityIndicators") case .segments: return UIStoryboard.inputElements.instantiateViewController(withIdentifier: "Segments") case .pickers: diff --git a/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/InputElements.storyboard b/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/InputElements.storyboard index a262957476..a24c6b3bc3 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/InputElements.storyboard +++ b/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/InputElements.storyboard @@ -624,6 +624,7 @@ + @@ -634,6 +635,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -649,25 +749,25 @@ - + - + - + - + - + - + - + diff --git a/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/UnsupportedViews.storyboard b/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/UnsupportedViews.storyboard index d8114b7e87..63d2571613 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/UnsupportedViews.storyboard +++ b/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/UnsupportedViews.storyboard @@ -17,23 +17,14 @@ - - - - - + @@ -43,14 +34,10 @@ - - - + - - diff --git a/DatadogSessionReplay/SRSnapshotTests/SRHost/MenuViewController.swift b/DatadogSessionReplay/SRSnapshotTests/SRHost/MenuViewController.swift index 3f196d1585..7e8a05cfdc 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRHost/MenuViewController.swift +++ b/DatadogSessionReplay/SRSnapshotTests/SRHost/MenuViewController.swift @@ -18,6 +18,8 @@ internal extension Fixture { return "Sliders" case .progressViews: return "Progress Views" + case .activityIndicators: + return "Activty Indicators" case .segments: return "Segments" case .pickers: diff --git a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/SRSnapshotTests.swift b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/SRSnapshotTests.swift index 1e63b46f4d..e5916a8e8b 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/SRSnapshotTests.swift +++ b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/SRSnapshotTests.swift @@ -67,6 +67,19 @@ final class SRSnapshotTests: SnapshotTestCase { } } + func testActivityIndicators() throws { + show(fixture: .activityIndicators) + + try forPrivacyModes([.allow, .mask]) { privacyMode in + let image = try takeSnapshot(with: privacyMode) + DDAssertSnapshotTest( + newImage: image, + snapshotLocation: .folder(named: snapshotsFolderPath, fileNameSuffix: "-\(privacyMode)-privacy"), + record: recordingMode + ) + } + } + func testSegments() throws { show(fixture: .segments) diff --git a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorder.swift b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorder.swift new file mode 100644 index 0000000000..9841027688 --- /dev/null +++ b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorder.swift @@ -0,0 +1,71 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +#if os(iOS) +import UIKit + +internal struct UIActivityIndicatorRecorder: NodeRecorder { + let identifier = UUID() + + func semantics(of view: UIView, with attributes: ViewAttributes, in context: ViewTreeRecordingContext) -> NodeSemantics? { + guard let activityIndicator = view as? UIActivityIndicatorView else { + return nil + } + + guard attributes.isVisible else { + return InvisibleElement.constant + } + + guard activityIndicator.isAnimating || !activityIndicator.hidesWhenStopped else { + return InvisibleElement.constant + } + + let builder = UIActivityIndicatorWireframesBuilder( + attributes: attributes, + wireframeID: context.ids.nodeID(view: activityIndicator, nodeRecorder: self), + backgroundColor: activityIndicator.backgroundColor?.cgColor + ) + + let node = Node(viewAttributes: attributes, wireframesBuilder: builder) + let allNodes = [node] + recordSubtree(of: activityIndicator, in: context) + return SpecificElement(subtreeStrategy: .ignore, nodes: allNodes) + } + + private func recordSubtree(of activityIndicator: UIActivityIndicatorView, in context: ViewTreeRecordingContext) -> [Node] { + let subtreeViewRecorder = ViewTreeRecorder( + nodeRecorders: [ + UIImageViewRecorder( + shouldRecordImagePredicate: { imageView in + return imageView.image == nil ? false : true + } + ) + ] + ) + + return subtreeViewRecorder.record(activityIndicator, in: context) + } +} + +internal struct UIActivityIndicatorWireframesBuilder: NodeWireframesBuilder { + var wireframeRect: CGRect { attributes.frame } + let attributes: ViewAttributes + + let wireframeID: WireframeID + let backgroundColor: CGColor? + + func buildWireframes(with builder: WireframesBuilder) -> [SRWireframe] { + return [ + builder.createShapeWireframe( + id: wireframeID, + frame: wireframeRect, + backgroundColor: backgroundColor, + cornerRadius: attributes.layerCornerRadius, + opacity: attributes.alpha + ) + ] + } +} +#endif diff --git a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorder.swift b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorder.swift index fbed39de28..39beee50ca 100644 --- a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorder.swift +++ b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorder.swift @@ -15,8 +15,7 @@ internal struct UnsupportedViewRecorder: NodeRecorder { private let unsupportedViewsPredicates: [(UIView, ViewTreeRecordingContext) -> Bool] = [ { _, context in context.viewControllerContext.isRootView(of: .safari) }, { _, context in context.viewControllerContext.isRootView(of: .activity) }, - { _, context in context.viewControllerContext.isRootView(of: .swiftUI) }, - { view, _ in view is UIActivityIndicatorView } + { _, context in context.viewControllerContext.isRootView(of: .swiftUI) } ] // swiftlint:enable opening_brace diff --git a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeSnapshotBuilder.swift b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeSnapshotBuilder.swift index ec1743bde9..5f3b2fff44 100644 --- a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeSnapshotBuilder.swift +++ b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/ViewTreeSnapshotBuilder.swift @@ -73,7 +73,8 @@ internal func createDefaultNodeRecorders() -> [NodeRecorder] { UIPickerViewRecorder(), UIDatePickerRecorder(), WKWebViewRecorder(), - UIProgressViewRecorder() + UIProgressViewRecorder(), + UIActivityIndicatorRecorder() ] } #endif diff --git a/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorderTests.swift b/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorderTests.swift new file mode 100644 index 0000000000..e0e3e871bc --- /dev/null +++ b/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorderTests.swift @@ -0,0 +1,74 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +#if os(iOS) +import XCTest +@_spi(Internal) +@testable import DatadogSessionReplay + +class UIActivityIndicatorRecorderTests: XCTestCase { + private let recorder = UIActivityIndicatorRecorder() + private let activityIndicator = UIActivityIndicatorView() + private var viewAttributes: ViewAttributes = .mockAny() + + func testWhenActivityIndicatorIsNotVisible() throws { + // When + viewAttributes = .mock(fixture: .invisible) + + // Then + let semantics = try XCTUnwrap(recorder.semantics(of: activityIndicator, with: viewAttributes, in: .mockAny())) + XCTAssertTrue(semantics is InvisibleElement) + } + + func testWhenActivityIndicatorIsVisible() throws { + // When + viewAttributes = .mock(fixture: .visible()) + activityIndicator.startAnimating() + + // Then + let semantics = try XCTUnwrap(recorder.semantics(of: activityIndicator, with: viewAttributes, in: .mockAny()) as? SpecificElement) + XCTAssertEqual(semantics.subtreeStrategy, .ignore, "Activity Indicator's subtree should not be recorded") + let builder = try XCTUnwrap(semantics.nodes.first?.wireframesBuilder as? UIActivityIndicatorWireframesBuilder) + XCTAssertEqual(builder.attributes, viewAttributes) + } + + func testWhenActivityIndicatorIsInvisible_WhenIsNotAnimatingAndHidesWhenStopped() throws { + // Given + activityIndicator.hidesWhenStopped = true + activityIndicator.stopAnimating() + + // When + viewAttributes = .mock(fixture: .visible()) + + // Then + let semantics = try XCTUnwrap(recorder.semantics(of: activityIndicator, with: viewAttributes, in: .mockAny())) + XCTAssertTrue(semantics is InvisibleElement) + } + + func testWhenActivityIndicatorIsVisible_WhenIsNotAnimatingAndDoesntHideWhenStopped() throws { + // Given + activityIndicator.hidesWhenStopped = false + activityIndicator.stopAnimating() + + // When + viewAttributes = .mock(fixture: .visible()) + + // Then + let semantics = try XCTUnwrap(recorder.semantics(of: activityIndicator, with: viewAttributes, in: .mockAny()) as? SpecificElement) + XCTAssertEqual(semantics.subtreeStrategy, .ignore, "Activity Indicator's subtree should not be recorded") + let builder = try XCTUnwrap(semantics.nodes.first?.wireframesBuilder as? UIActivityIndicatorWireframesBuilder) + XCTAssertEqual(builder.attributes, viewAttributes) + } + + func testWhenViewIsNotOfExpectedType() { + // When + let view = UITextField() + + // Then + XCTAssertNil(recorder.semantics(of: view, with: viewAttributes, in: .mockAny())) + } +} +#endif diff --git a/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift b/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift index 03bb980cff..19a5f715aa 100644 --- a/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift +++ b/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift @@ -17,10 +17,9 @@ class UnsupportedViewRecorderTests: XCTestCase { private let recorder = UnsupportedViewRecorder() private let unsupportedViews: [UIView] = [ - UIActivityIndicatorView() ].compactMap { $0 } private let expectedUnsupportedViewsClassNames = [ - "UIActivityIndicatorView", "WKWebView" + "WKWebView" ] private let otherViews = [UILabel(), UIView(), UIImageView(), UIScrollView(), WKWebView()] From d2a31589973fd08743cdefa03526d038bcff880f Mon Sep 17 00:00:00 2001 From: Marie Denis Date: Tue, 2 Jul 2024 10:45:58 +0200 Subject: [PATCH 2/6] RUM-4150 [SR] Update snapshot tests --- .../Resources/Storyboards/InputElements.storyboard | 2 +- .../SRSnapshotTests/SRSnapshotTests.swift | 11 +---------- .../SRSnapshotTests/Utils/SnapshotTestCase.swift | 2 ++ .../testActivityIndicators()-allow-privacy.png.json | 1 + .../testActivityIndicators()-mask-privacy.png.json | 1 + .../testUnsupportedView()-allow-privacy.png.json | 2 +- 6 files changed, 7 insertions(+), 12 deletions(-) create mode 100644 DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testActivityIndicators()-allow-privacy.png.json create mode 100644 DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testActivityIndicators()-mask-privacy.png.json diff --git a/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/InputElements.storyboard b/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/InputElements.storyboard index a24c6b3bc3..b89cf8dfde 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/InputElements.storyboard +++ b/DatadogSessionReplay/SRSnapshotTests/SRFixtures/Sources/SRFixtures/Resources/Storyboards/InputElements.storyboard @@ -466,7 +466,7 @@ - + diff --git a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/SRSnapshotTests.swift b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/SRSnapshotTests.swift index c018400393..a42531cf2a 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/SRSnapshotTests.swift +++ b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/SRSnapshotTests.swift @@ -35,16 +35,7 @@ final class SRSnapshotTests: SnapshotTestCase { } func testActivityIndicators() throws { - show(fixture: .activityIndicators) - - try forPrivacyModes([.allow, .mask]) { privacyMode in - let image = try takeSnapshot(with: privacyMode) - DDAssertSnapshotTest( - newImage: image, - snapshotLocation: .folder(named: snapshotsFolderPath, fileNameSuffix: "-\(privacyMode)-privacy"), - record: recordingMode - ) - } + try takeSnapshotFor(.activityIndicators, with: [.allow, .mask], shouldRecord: shouldRecord, folderPath: snapshotsFolderPath) } func testSegments() throws { diff --git a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/Utils/SnapshotTestCase.swift b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/Utils/SnapshotTestCase.swift index de35fa8003..bb7c4497c0 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/Utils/SnapshotTestCase.swift +++ b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/Utils/SnapshotTestCase.swift @@ -46,6 +46,8 @@ internal class SnapshotTestCase: XCTestCase { function: StaticString = #function ) throws { show(fixture: fixture) + // Give time for the view to appear and lay out properly + wait(seconds: 0.2) try forPrivacyModes(privacyModes) { privacyMode in let image = try takeSnapshot(with: privacyMode) diff --git a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testActivityIndicators()-allow-privacy.png.json b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testActivityIndicators()-allow-privacy.png.json new file mode 100644 index 0000000000..c030eab54c --- /dev/null +++ b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testActivityIndicators()-allow-privacy.png.json @@ -0,0 +1 @@ +{"hash":"1cf8a7b028af17c1c09e4df27dea1d70fe742a4e"} \ No newline at end of file diff --git a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testActivityIndicators()-mask-privacy.png.json b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testActivityIndicators()-mask-privacy.png.json new file mode 100644 index 0000000000..ff48ee9646 --- /dev/null +++ b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testActivityIndicators()-mask-privacy.png.json @@ -0,0 +1 @@ +{"hash":"70d431c6e2aea43e03cc26d68577e98c55172986"} \ No newline at end of file diff --git a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testUnsupportedView()-allow-privacy.png.json b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testUnsupportedView()-allow-privacy.png.json index 2798a7661c..e9c762901f 100644 --- a/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testUnsupportedView()-allow-privacy.png.json +++ b/DatadogSessionReplay/SRSnapshotTests/SRSnapshotTests/_snapshots_/pointers/testUnsupportedView()-allow-privacy.png.json @@ -1 +1 @@ -{"hash":"34204d9f653bc095c944a08f43f50f437a203cd2"} \ No newline at end of file +{"hash":"4737e8cc121b02e4f36c6adcceadc6acd8d30a98"} \ No newline at end of file From c56ff7bb87179d9481a56c29cd0468554ac7b85d Mon Sep 17 00:00:00 2001 From: Marie Denis Date: Tue, 2 Jul 2024 10:57:40 +0200 Subject: [PATCH 3/6] RUM-4150 [SR] Fix project.pbxproj --- Datadog/Datadog.xcodeproj/project.pbxproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index f769710802..e5c9f41ff7 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -659,6 +659,8 @@ 61FDBA15269722B4001D9D43 /* CrashReportMinifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FDBA14269722B4001D9D43 /* CrashReportMinifierTests.swift */; }; 61FDBA1726974CA9001D9D43 /* DDCrashReportBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FDBA1626974CA9001D9D43 /* DDCrashReportBuilderTests.swift */; }; 61FF282824B8A31E000B3D9B /* RUMEventMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FF282724B8A31E000B3D9B /* RUMEventMatcher.swift */; }; + 969B3B212C33F80500D62400 /* UIActivityIndicatorRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969B3B202C33F80500D62400 /* UIActivityIndicatorRecorder.swift */; }; + 969B3B232C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 969B3B222C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift */; }; 96E414142C2AF56F005A6119 /* UIProgressViewRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E414132C2AF56F005A6119 /* UIProgressViewRecorder.swift */; }; 96E414162C2AF5C1005A6119 /* UIProgressViewRecorderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E414152C2AF5C1005A6119 /* UIProgressViewRecorderTests.swift */; }; 9E55407C25812D1C00F6E3AD /* RUM+objc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E55407B25812D1C00F6E3AD /* RUM+objc.swift */; }; @@ -2686,6 +2688,8 @@ 61FF282F24BC5E2D000B3D9B /* RUMEventFileOutputTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMEventFileOutputTests.swift; sourceTree = ""; }; 61FF416125EE5FF400CE35EC /* CrashLogReceiverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashLogReceiverTests.swift; sourceTree = ""; }; 61FF9A4425AC5DEA001058CC /* ViewIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewIdentifier.swift; sourceTree = ""; }; + 969B3B202C33F80500D62400 /* UIActivityIndicatorRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorRecorder.swift; sourceTree = ""; }; + 969B3B222C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityIndicatorRecorderTests.swift; sourceTree = ""; }; 96E414132C2AF56F005A6119 /* UIProgressViewRecorder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewRecorder.swift; sourceTree = ""; }; 96E414152C2AF5C1005A6119 /* UIProgressViewRecorderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIProgressViewRecorderTests.swift; sourceTree = ""; }; 9E0542CA25F8EBBE007A3D0B /* Kronos.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Kronos.xcframework; path = ../Carthage/Build/Kronos.xcframework; sourceTree = ""; }; @@ -3609,6 +3613,7 @@ 61054E272A6EE10A00AAA894 /* NodeRecorders */ = { isa = PBXGroup; children = ( + 969B3B202C33F80500D62400 /* UIActivityIndicatorRecorder.swift */, 61054E282A6EE10A00AAA894 /* UIDatePickerRecorder.swift */, 61054E292A6EE10A00AAA894 /* UITextViewRecorder.swift */, 61054E2A2A6EE10A00AAA894 /* UIImageViewRecorder.swift */, @@ -3884,6 +3889,7 @@ 61054F692A6EE1BA00AAA894 /* NodeRecorders */ = { isa = PBXGroup; children = ( + 969B3B222C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift */, 61054F6A2A6EE1BA00AAA894 /* UILabelRecorderTests.swift */, 61054F6B2A6EE1BA00AAA894 /* UITextFieldRecorderTests.swift */, 61054F6C2A6EE1BA00AAA894 /* UITabBarRecorderTests.swift */, @@ -8248,6 +8254,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 969B3B212C33F80500D62400 /* UIActivityIndicatorRecorder.swift in Sources */, 61054EA22A6EE10B00AAA894 /* Scheduler.swift in Sources */, A7EA88562B17639A00FE2580 /* ResourcesWriter.swift in Sources */, A7B932FB2B1F6A0A00AE6477 /* EnrichedRecord.swift in Sources */, @@ -8331,6 +8338,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 969B3B232C33F81E00D62400 /* UIActivityIndicatorRecorderTests.swift in Sources */, 61054FD42A6EE1BA00AAA894 /* SegmentRequestBuilderTests.swift in Sources */, 61054FB52A6EE1BA00AAA894 /* UISliderRecorderTests.swift in Sources */, 61054FB22A6EE1BA00AAA894 /* UILabelRecorderTests.swift in Sources */, From e8b46895a79b783782724d5e0654b577985dce5c Mon Sep 17 00:00:00 2001 From: Marie Denis Date: Tue, 2 Jul 2024 14:37:40 +0200 Subject: [PATCH 4/6] RUM-4150 Pin Otel version in Carfile --- Cartfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile b/Cartfile index 31328b20d2..e3155c952c 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ github "microsoft/plcrashreporter" ~> 1.11.2 -binary "https://raw.githubusercontent.com/DataDog/opentelemetry-swift-packages/main/OpenTelemetryApi.json" ~> 1.6.0 +binary "https://raw.githubusercontent.com/DataDog/opentelemetry-swift-packages/main/OpenTelemetryApi.json" == 1.6.0 From 5e248fd5610b8cfcbfe4a45b59b7ffbb99bfe5b3 Mon Sep 17 00:00:00 2001 From: Marie Denis Date: Wed, 3 Jul 2024 16:14:53 +0200 Subject: [PATCH 5/6] RUM-4150 Address comments --- .../UIActivityIndicatorRecorder.swift | 4 +- .../UnsupportedViewRecorderTests.swift | 38 ------------------- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorder.swift b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorder.swift index 9841027688..e9c592144f 100644 --- a/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorder.swift +++ b/DatadogSessionReplay/Sources/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UIActivityIndicatorRecorder.swift @@ -38,9 +38,7 @@ internal struct UIActivityIndicatorRecorder: NodeRecorder { let subtreeViewRecorder = ViewTreeRecorder( nodeRecorders: [ UIImageViewRecorder( - shouldRecordImagePredicate: { imageView in - return imageView.image == nil ? false : true - } + shouldRecordImagePredicate: { $0.image != nil } ) ] ) diff --git a/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift b/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift index 19a5f715aa..598dcad634 100644 --- a/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift +++ b/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift @@ -16,47 +16,9 @@ import SafariServices class UnsupportedViewRecorderTests: XCTestCase { private let recorder = UnsupportedViewRecorder() - private let unsupportedViews: [UIView] = [ - ].compactMap { $0 } - private let expectedUnsupportedViewsClassNames = [ - "WKWebView" - ] - private let otherViews = [UILabel(), UIView(), UIImageView(), UIScrollView(), WKWebView()] - /// `ViewAttributes` simulating common attributes of the view. private var viewAttributes: ViewAttributes = .mockAny() - func testWhenViewIsNotVisible() throws { - // When - viewAttributes = .mock(fixture: .invisible) - - // Then - try unsupportedViews.forEach { view in - let semantics = try XCTUnwrap(recorder.semantics(of: view, with: viewAttributes, in: .mockAny())) - XCTAssertTrue(semantics is InvisibleElement) - } - otherViews.forEach { view in - XCTAssertNil(recorder.semantics(of: view, with: viewAttributes, in: .mockAny())) - } - } - - func testWhenViewIsVisible() throws { - // When - viewAttributes = .mock(fixture: .visible([.noAppearance, .someAppearance].randomElement()!)) - - // Then - try unsupportedViews.enumerated().forEach { index, view in - let semantics = try XCTUnwrap(recorder.semantics(of: view, with: viewAttributes, in: .mockAny())) - XCTAssertTrue(semantics is SpecificElement) - XCTAssertEqual(semantics.subtreeStrategy, .ignore) - let wireframeBuilder = try XCTUnwrap(semantics.nodes.first?.wireframesBuilder as? UnsupportedViewWireframesBuilder) - XCTAssertEqual(wireframeBuilder.unsupportedClassName, expectedUnsupportedViewsClassNames[index]) - } - otherViews.forEach { view in - XCTAssertNil(recorder.semantics(of: view, with: viewAttributes, in: .mockAny())) - } - } - func testWhenViewIsUnsupportedViewControllersRootView() throws { var context = ViewTreeRecordingContext.mockRandom() context.viewControllerContext.isRootView = true From 530c28e2fd5638dd4d8cc9fd92a63508981b9545 Mon Sep 17 00:00:00 2001 From: Marie Denis Date: Wed, 3 Jul 2024 16:25:46 +0200 Subject: [PATCH 6/6] RUM-4150 Remove unused line --- .../NodeRecorders/UnsupportedViewRecorderTests.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift b/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift index 598dcad634..769b18fb4c 100644 --- a/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift +++ b/DatadogSessionReplay/Tests/Recorder/ViewTreeSnapshotProducer/ViewTreeSnapshot/NodeRecorders/UnsupportedViewRecorderTests.swift @@ -16,9 +16,6 @@ import SafariServices class UnsupportedViewRecorderTests: XCTestCase { private let recorder = UnsupportedViewRecorder() - /// `ViewAttributes` simulating common attributes of the view. - private var viewAttributes: ViewAttributes = .mockAny() - func testWhenViewIsUnsupportedViewControllersRootView() throws { var context = ViewTreeRecordingContext.mockRandom() context.viewControllerContext.isRootView = true