Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add warning for experimental feature #4869

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Plans/iOS-SwiftUI_Base.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"containerPath" : "container:iOS-SwiftUI.xcodeproj",
"identifier" : "7BB6224826A56C4E00D0E75E",
"name" : "iOS-SwiftUI"
}
},
"uiTestingScreenshotsLifetime" : "keepAlways"
},
"testTargets" : [
{
Expand Down
85 changes: 84 additions & 1 deletion Samples/iOS-SwiftUI/iOS-SwiftUI-UITests/LaunchUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,94 @@
XCTAssertEqual(app.staticTexts["SPAN_ID"].label, "NO SPAN")
}

func testTTID_TTFD() {
func testTTID_TTFD_withTracedViewWaitForFullDisplay_shouldBeReported() {
// -- Arrange --
let app = XCUIApplication()
app.launch()

// -- Act --
app.buttons["Show TTD"].tap()

// -- Assert --
// By pressing the button 'Show TTD', it will display the TTD info.
// If the TTD info is not displayed, it was not reported, therefore the test should fail.
XCTAssertEqual(app.staticTexts["TTDInfo"].label, "TTID and TTFD found")
}

func testTTID_TTFD_withDelayedFullDisplay_shouldReportTTDInfoOnlyAfterDelay() {
// -- Arrange --
// Launch the app and navigate to the delayed full display view.
let app = XCUIApplication()
app.launch()
app.buttons["button.destination.full-display-deplayed"].tap()

// Note: UI Tests can not directly access Sentry SDK data, therefore we
// need to set the status in the UI and access it from there.
let updateStatusButton = app.buttons["button.update-ttfd-ttid-status"]
guard updateStatusButton.waitForExistence(timeout: 1) else {
return XCTFail("Update status button not found")
}
// Switchs can only have two states: On or Off
// Therefore we add an additional counter to check if the status has been updated.
let statusRefreshCounter = app.staticTexts["label.status-refresh-counter"]
guard statusRefreshCounter.waitForExistence(timeout: 1) else {
return XCTFail("Status refresh counter not found")
}
XCTAssertEqual(statusRefreshCounter.label, "Status Refresh Counter: 0")
let statusSwitchTTID = app.switches["check.ttid-reported"]
guard statusSwitchTTID.waitForExistence(timeout: 1) else {
return XCTFail("TTID status Switch not found")
}
XCTAssertEqual(statusSwitchTTID.value as? String, "0")
let statusSwitchTTFD = app.switches["check.ttfd-reported"]
guard statusSwitchTTFD.waitForExistence(timeout: 1) else {
return XCTFail("TTFD status Switch not found")
}
XCTAssertEqual(statusSwitchTTID.value as? String, "0")

// - Check Preconditions
// Expect the initial content to appear immediately.
let initialContent = app.staticTexts["content.initial"]
guard initialContent.waitForExistence(timeout: 1) else {
return XCTFail("Initial content not found")
}

// Confirm pre-condition that full content does not exist yet.
let fullContent = app.staticTexts["content.delayed"]
if fullContent.exists {
return XCTFail("Delayed content should not exist yet")
}

// Verify TTID has been reported, but TTFD not yet.
updateStatusButton.tap()
XCTAssertEqual(statusRefreshCounter.label, "Status Refresh Counter: 1")
XCTAssertEqual(statusSwitchTTID.value as? String, "1")
XCTAssertEqual(statusSwitchTTFD.value as? String, "0")

Check failure on line 104 in Samples/iOS-SwiftUI/iOS-SwiftUI-UITests/LaunchUITests.swift

View workflow job for this annotation

GitHub Actions / UI Tests for SwiftUI on iPhone 8 (16.1) Simulator

testTTID_TTFD_withDelayedFullDisplay_shouldReportTTDInfoOnlyAfterDelay, XCTAssertEqual failed: ("Optional("1")") is not equal to ("Optional("0")")

Check failure on line 104 in Samples/iOS-SwiftUI/iOS-SwiftUI-UITests/LaunchUITests.swift

View workflow job for this annotation

GitHub Actions / UI Tests for SwiftUI on iPhone 8 (16.1) Simulator

testTTID_TTFD_withDelayedFullDisplay_shouldReportTTDInfoOnlyAfterDelay, XCTAssertEqual failed: ("Optional("1")") is not equal to ("Optional("0")")

// -- Act --
// Trigger the appearance of the delayed content
let triggerButton = app.buttons["button.trigger-delayed-content"]
guard triggerButton.waitForExistence(timeout: 1) else {
return XCTFail("Trigger button not found")
}
triggerButton.tap()

// -- Assert --
// Verify TTFD is delayed and not reported yet.
updateStatusButton.tap()
XCTAssertEqual(statusRefreshCounter.label, "Status Refresh Counter: 2")
XCTAssertEqual(statusSwitchTTID.value as? String, "1")
XCTAssertEqual(statusSwitchTTFD.value as? String, "0")

// Confirm that the full content eventually appears.
guard fullContent.waitForExistence(timeout: 5) else {
return XCTFail("Delayed content not found")
}

// Verify TTFD has been reported.
updateStatusButton.tap()
XCTAssertEqual(statusRefreshCounter.label, "Status Refresh Counter: 3")
XCTAssertEqual(statusSwitchTTID.value as? String, "1")
XCTAssertEqual(statusSwitchTTFD.value as? String, "1")
}
}
8 changes: 6 additions & 2 deletions Samples/iOS-SwiftUI/iOS-SwiftUI.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
7BB6225E26A56CB600D0E75E /* Sentry.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7BB6225C26A56CB600D0E75E /* Sentry.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
84BA72B52C9369C80045B828 /* GitInjections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BA72B32C9369C80045B828 /* GitInjections.swift */; };
84D4FEB528ECD53500EDAAFE /* Sentry.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D4FEB228ECD52E00EDAAFE /* Sentry.framework */; };
D4DB99952D678355000AD63F /* DelayedFullDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB998D2D67834D000AD63F /* DelayedFullDisplayView.swift */; };
D8199DCD29376FD90074249E /* SentrySwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8BBD38B2901AE400011F850 /* SentrySwiftUI.framework */; };
D8199DCE29376FD90074249E /* SentrySwiftUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D8BBD38B2901AE400011F850 /* SentrySwiftUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
D832FAF02982A908007A9A5F /* FormScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D832FAEF2982A908007A9A5F /* FormScreen.swift */; };
Expand Down Expand Up @@ -171,6 +172,7 @@
84D4FEA628ECD51800EDAAFE /* Sentry.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Sentry.framework; sourceTree = BUILT_PRODUCTS_DIR; };
84D4FEA828ECD52700EDAAFE /* Sentry.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Sentry.xcodeproj; path = ../../Sentry.xcodeproj; sourceTree = "<group>"; };
84D4FEAA28ECD52E00EDAAFE /* Sentry.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Sentry.xcodeproj; path = "/Users/andrewmcknight/Code/organization/getsentry/repos/public/sentry-cocoa/Sentry.xcodeproj"; sourceTree = "<absolute>"; };
D4DB998D2D67834D000AD63F /* DelayedFullDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelayedFullDisplayView.swift; sourceTree = "<group>"; };
D832FAEF2982A908007A9A5F /* FormScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormScreen.swift; sourceTree = "<group>"; };
D85388D02980222500B63908 /* UIKitScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitScreen.swift; sourceTree = "<group>"; };
D8A22A7729151DB7006907D9 /* bridging-headers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "bridging-headers.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -211,7 +213,7 @@
8425DE232B52241000113FEF /* SentryProfilerTests.xctest */,
8425DE252B52241000113FEF /* libSentryTestUtils.a */,
8425DE272B52241000113FEF /* SentryTestUtilsDynamic.framework */,
D833D61B2D13216300961E7A /* libSentrySwiftUITests.a */,
D833D61B2D13216300961E7A /* SentrySwiftUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
Expand Down Expand Up @@ -249,6 +251,7 @@
7BB6224B26A56C4E00D0E75E /* iOS-SwiftUI */ = {
isa = PBXGroup;
children = (
D4DB998D2D67834D000AD63F /* DelayedFullDisplayView.swift */,
7BB6226026A56E1E00D0E75E /* iOS-SwiftUI.entitlements */,
7BB6224C26A56C4E00D0E75E /* SwiftUIApp.swift */,
7BB6224E26A56C4E00D0E75E /* ContentView.swift */,
Expand Down Expand Up @@ -485,7 +488,7 @@
remoteRef = 84D4FEB328ECD52E00EDAAFE /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
D833D61B2D13216300961E7A /* libSentrySwiftUITests.a */ = {
D833D61B2D13216300961E7A /* SentrySwiftUITests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = SentrySwiftUITests.xctest;
Expand Down Expand Up @@ -576,6 +579,7 @@
D85388D12980222500B63908 /* UIKitScreen.swift in Sources */,
7BB6224F26A56C4E00D0E75E /* ContentView.swift in Sources */,
7B5DA9D92859DC850069AD02 /* LoremIpsumView.swift in Sources */,
D4DB99952D678355000AD63F /* DelayedFullDisplayView.swift in Sources */,
D832FAF02982A908007A9A5F /* FormScreen.swift in Sources */,
84BA72B52C9369C80045B828 /* GitInjections.swift in Sources */,
7BB6224D26A56C4E00D0E75E /* SwiftUIApp.swift in Sources */,
Expand Down
5 changes: 5 additions & 0 deletions Samples/iOS-SwiftUI/iOS-SwiftUI/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ struct ContentView: View {
NavigationLink(destination: FormScreen()) {
Text("Form Screen")
}

NavigationLink(destination: DelayedFullDisplayView()) {
Text("Delayed Full Display")
.accessibilityIdentifier("button.destination.full-display-deplayed")
}
}
.background(Color.white)
}
Expand Down
70 changes: 70 additions & 0 deletions Samples/iOS-SwiftUI/iOS-SwiftUI/DelayedFullDisplayView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import SentrySwiftUI
import SwiftUI

struct DelayedFullDisplayView: View {

@State private var isDelayedContentVisible = false

@State private var statusRefreshCounter = 0
@State private var isTTIDReported = false
@State private var isTTFDReported = false

var body: some View {
VStack {
SentryTracedView("Content", waitForFullDisplay: true) {
Text("Initial Content")
.accessibilityIdentifier("content.initial")
Button("Show Delayed Content") {
// Cause a custom delay to simulate loading content
// The full content will then report that it is fully displayed
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
isDelayedContentVisible = true
}
}
.accessibilityIdentifier("button.trigger-delayed-content")
if isDelayedContentVisible {
Text("Delayed Content")
.accessibilityIdentifier("content.delayed")
.onAppear {
SentrySDK.reportFullyDisplayed()
}
} else {
ProgressView()
}
}
Spacer()
VStack {
Button("Refresh TTFD / TTID Status") {
guard let tracer = SentrySDK.span as? SentryTracer else {
return
}
// Check if the spans are found in the current tracer
// Afterwards increment the counter so we can definitely tell that the status was refreshed
isTTIDReported = isTTIDSpanFound(tracer: tracer)
isTTFDReported = isTTFDSpanFound(tracer: tracer)
statusRefreshCounter += 1
}
.accessibilityIdentifier("button.update-ttfd-ttid-status")
Text("Status Refresh Counter: \(statusRefreshCounter)")
.accessibilityIdentifier("label.status-refresh-counter")
Toggle(isOn: $isTTIDReported) {
Text("TTID Reported")
}
.accessibilityIdentifier("check.ttid-reported")
Toggle(isOn: $isTTFDReported) {
Text("TTFD Reported")
}
.accessibilityIdentifier("check.ttfd-reported")
}
.font(.caption)
}
}

func isTTIDSpanFound(tracer: SentryTracer) -> Bool {
tracer.children.contains { $0.spanDescription?.contains("initial display") == true } == true
}

func isTTFDSpanFound(tracer: SentryTracer) -> Bool {
tracer.children.contains { $0.spanDescription?.contains("full display") == true } == true
}
}
16 changes: 15 additions & 1 deletion Sources/SentrySwiftUI/SentryTracedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,28 @@
let content: () -> Content

#if canImport(SwiftUI) && canImport(UIKit) && os(iOS) || os(tvOS)
/// Creates a view that measures the performance of its `content`.
///
/// - Parameter viewName: The name that will be used for the span, if nil we try to get the name of the content class.
/// - Parameter content: The content that you want to track the performance
public init(_ viewName: String? = nil, @ViewBuilder content: @escaping () -> Content) {
self.content = content

Check warning on line 126 in Sources/SentrySwiftUI/SentryTracedView.swift

View check run for this annotation

Codecov / codecov/patch

Sources/SentrySwiftUI/SentryTracedView.swift#L126

Added line #L126 was not covered by tests
let name = viewName ?? SentryTracedView.extractName(content: Content.self)
let nameSource = viewName == nil ? SentryTransactionNameSource.component : SentryTransactionNameSource.custom
let initialViewModel = SentryTraceViewModel(name: name, nameSource: nameSource, waitForFullDisplay: nil)
_viewModel = State(initialValue: initialViewModel)
}

Check warning on line 131 in Sources/SentrySwiftUI/SentryTracedView.swift

View check run for this annotation

Codecov / codecov/patch

Sources/SentrySwiftUI/SentryTracedView.swift#L129-L131

Added lines #L129 - L131 were not covered by tests

/// Creates a view that measures the performance of its `content`.
///
/// - Parameter viewName: The name that will be used for the span, if nil we try to get the name of the content class.
/// - Parameter waitForFullDisplay: Indicates whether this view transaction should wait for `SentrySDK.reportFullyDisplayed()`
/// in case you need to track some asyncronous task. This is ignored for any `SentryTracedView` that is child of another `SentryTracedView`.
/// If nil, it will use the `enableTimeToFullDisplayTracing` option from the SDK.
/// - Parameter content: The content that you want to track the performance
public init(_ viewName: String? = nil, waitForFullDisplay: Bool? = nil, @ViewBuilder content: @escaping () -> Content) {
///
/// - Experiment: This initializer is an experimental feature and may still have bugs.
public init(_ viewName: String? = nil, waitForFullDisplay: Bool?, @ViewBuilder content: @escaping () -> Content) {
self.content = content
let name = viewName ?? SentryTracedView.extractName(content: Content.self)
let nameSource = viewName == nil ? SentryTransactionNameSource.component : SentryTransactionNameSource.custom
Expand Down
Loading