Skip to content

test(paywalls): regression tests for workflow exit offer surviving non-content reconfigures#6919

Closed
vegaro wants to merge 6 commits into
mainfrom
cesar/paywall-vc-exit-offer-regression-tests
Closed

test(paywalls): regression tests for workflow exit offer surviving non-content reconfigures#6919
vegaro wants to merge 6 commits into
mainfrom
cesar/paywall-vc-exit-offer-regression-tests

Conversation

@vegaro

@vegaro vegaro commented Jun 5, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds three unit tests to PaywallViewControllerExitOfferTests covering the exit-offer-vs-reconfigure interaction identified in code review of #facu/workflow-exit-offer-uikit-bridge
  • Exposes minimal @_spi(Internal) test hooks on PaywallViewController so tests can seed and inspect private exit offer state without a scheme launch argument
  • Extracts workflowsEndpointEnabled as an overridable var so test subclasses can control the flag

Context

Code review of the UIKit workflow exit offer bridge (facu/workflow-exit-offer-uikit-bridge) flagged a Critical issue: configuration.didSet fires on all property mutations — including non-content ones like update(with displayCloseButton:) and updateFont(with:) — which now calls updateWorkflowExitOffer(nil) and rebuilds the hosting controller, creating a race window where the exit offer is cleared mid-session.

These tests document the expected behavior before the fix lands:

Test Expected Current
testUpdateDisplayCloseButtonDoesNotClearWorkflowExitOffer exit offer survives ❌ fails (bug)
testUpdateFontDoesNotClearWorkflowExitOffer exit offer survives ❌ fails (bug)
testUpdateOfferingClearsWorkflowExitOffer exit offer cleared ✅ passes (intentional)

Once the didSet guard (guard configuration.content != oldValue.content else { return }) is applied in the parent branch, the first two tests will go green.

Test plan

  • Confirm the two failing tests (displayCloseButton, updateFont) fail on this branch as-is
  • Confirm all three pass after the didSet fix is applied in facu/workflow-exit-offer-uikit-bridge

🤖 Generated with Claude Code

facumenzella and others added 5 commits June 4, 2026 16:36
Feed the embedded SwiftUI paywall's step-aware workflow exit offer into the
UIKit controller's exitOfferOffering (via the exit-offer environment binding
plus the preference key), so swipe-to-dismiss and the close button surface it
through the existing exit-offer machinery. Exit-offer children bridge too, so a
workflow can open another workflow as its exit offer and chain further.

Clear the offer when the host is rebuilt under workflows so update(with:)
can't surface a stale exit offer during the reload window.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a "UIKit View Controller" entry to the Live Paywalls context menu that
modally presents the offering through PaywallViewController, so its UIKit
dismissal handling and the workflow exit-offer bridge can be exercised (the
SwiftUI PaywallView path does not go through that controller).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
configuration.didSet cleared exitOfferOffering directly, bypassing
updateWorkflowExitOffer's "don't clear while an exit offer is presenting"
guard. Route through it so a hybrid update(with:) during an in-flight
exit-offer dismiss can't drop the offer. Behavior is unchanged for the normal
reload case (not presenting) and remains a no-op under the legacy path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n-content reconfigures

Adds three tests that document expected behavior:
- update(with displayCloseButton:) must not clear exitOfferOffering
- updateFont(with:) must not clear exitOfferOffering
- update(with offering:) should clear exitOfferOffering (legitimate rebuild)

Also exposes workflowsEndpointEnabled as an overridable var and adds
@_spi(Internal) test hooks (exitOfferOfferingForTesting,
simulateWorkflowExitOfferUpdate) so tests don't need a scheme launch argument
and can inspect private state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Jun 5, 2026

Copy link
Copy Markdown

❌ Failures

RevenueCat.xcodeproj is out of sync.

The following Swift files were added but are missing from RevenueCat.xcodeproj:
Tests/RevenueCatUITests/UIKit/PaywallViewControllerExitOfferTests.swift

To fix: open RevenueCat.xcodeproj in Xcode, add/remove the files above in the appropriate target. Check where similar files in the same directory are assigned if you're unsure which target to use.

@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Jun 5, 2026

Copy link
Copy Markdown
2 Errors
🚫 Label the PR using one of the change type labels. If you are not sure which label to use, choose pr:other.
🚫 Purchases iOS checks failed — see the comment above for details.
Label Description
pr:feat A new feature. Use along with pr:breaking to force a major release.
pr:fix A bug fix. Use along with pr:force_minor to force a minor release.
pr:other Other changes. Catch-all for anything that doesn't fit the above categories. Releases that only contain this label will not be released. Use along with pr:force_patch, or pr:force_minor to force a patch or minor release.
pr:RevenueCatUI Use along any other tag to mark a PR that only contains RevenueCatUI changes
pr:next_release Preparing a new release
pr:dependencies Updating a dependency
pr:phc_dependencies Updating purchases-hybrid-common dependency
pr:changelog_ignore The PR will not be included in the changelog. This label doesn't determine the type of bump of the version and must be combined with pr:feat, pr:fix or pr:other.

Generated by 🚫 Danger

…lass init issues

Convenience inits on PaywallViewController aren't inherited by subclasses because
the class defines its own designated init(content:fonts:...). Replacing the overridable
computed var with a stored internal var lets tests set it directly on an instance,
removing the need for a subclass entirely.

Also drops the @_spi(Internal) annotation from the test hooks — @testable import
is sufficient since they only need to be internal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@facumenzella facumenzella force-pushed the facu/workflow-exit-offer-uikit-bridge branch 3 times, most recently from b359c54 to 67162d5 Compare June 8, 2026 14:48
Base automatically changed from facu/workflow-exit-offer-uikit-bridge to main June 8, 2026 14:59
@facumenzella

Copy link
Copy Markdown
Member

@vegaro I am closing this one. I've already included your suggestions in #6911 :)

@facumenzella facumenzella deleted the cesar/paywall-vc-exit-offer-regression-tests branch June 12, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants