Skip to content

test(ios): run the RN library XCTest suites, not just the placeholder - #890

Merged
eszlamczyk merged 3 commits into
mainfrom
fix/wire-ios-pill-tests
Oct 9, 2026
Merged

eszlamczyk merged 3 commits into
mainfrom
fix/wire-ios-pill-tests

Conversation

@eszlamczyk

@eszlamczyk eszlamczyk commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

What/Why?

Follow-up to #877.

#877 wired the rn-ios-unit-tests lane and said #862's real suites "plug into the same target on merge". That holds on Android - Gradle auto-discovers src/test/java, so LinkPillSpanTest runs with zero wiring the moment #862 lands. It did not hold on iOS, for three reasons this PR fixes.

1. Sources were not auto-discovered. EnrichedMarkdownExample.xcodeproj is objectVersion 54 and lists every source explicitly, so each __tests__/ios/*.mm needed a PBXFileReference, a PBXBuildFile, a group entry and a Sources-phase entry. A file nobody wired stayed silently unrun rather than failing, which is how the lane passed while covering nothing.

The test target now has a Generate library test sources build phase running scripts/generate-ios-test-sources.sh, which globs __tests__/ios and emits an include list that one wired umbrella source, EnrichedMarkdownExampleTests/ENRMLibraryTests.mm, pulls in. Dropping a .m/.mm in that directory is now the whole procedure, matching Android.

The tradeoff: every file there compiles into one translation unit, so file-scope helpers share a namespace and need distinct names. The ENRM prefix already does this for classes. Documented in the script header and the directory README.

2. The test target could not resolve library headers. HEADER_SEARCH_PATHS covered core/cpp/*, ios/internals and ios/input/internals only. Library headers import their siblings unqualified - ENRMLinkPillAttachment.h does #import "ENRMUIKit.h", which lives in ios/utils and was unreachable. Any real suite failed to build on the first import. This went unnoticed because EnrichedMarkdownExampleTests.m imports nothing from the library, so the lane was green without ever proving a single library header could be resolved.

vendor/ and generated/ are deliberately excluded - sweeping vendor in pulls three RaTeX xcframework module maps into scope and clang rejects the duplicate RaTeXFFI module definitions.

3. A green test:ios proved nothing about how much ran. scripts/test-ios.sh now counts the - (void)test prototypes under __tests__/ios and fails when fewer tests than that actually ran. It is an under-approximation by design (helper superclasses can add tests), so it catches a suite dropping out of the run without going stale as suites grow.

On -quiet

Worth recording, because the previous comment in test-ios.sh asserted the opposite. Measured on Xcode 27 by breaking testHarnessRuns on purpose:

with -quiet
Build errors printed
Names of failing tests printed
Assertion text, expected/actual, custom message dropped
Test counts dropped

So a red lane named the failing test and said nothing about why. Dropping -quiet would fix that by adding thousands of pod compile lines to every CI log, so instead the run asks for a -resultBundlePath and reads both the count and the failure text back out of it:

FAILED EnrichedMarkdownExampleTests/testHarnessRuns
        ((41) equal to (42)) failed: ("41") is not equal to ("42") - deliberate failure probe

When the build fails before any test runs the bundle is absent, the report is skipped, and xcodebuild has already said why.

Testing

Verified locally on Xcode 27 with a full yarn prepare + pod install per configuration:

  • This branch alone: Executed 1 test(s), exit 0. The generated include list is empty and the umbrella compiles to nothing.
  • This branch with feat: add opt-in native link pill presentation #862 merged in, unmodified: fails to compile at ENRMLinkPillTextStorageTests.mm:255. See below.
  • Same, with that one line fixed: Executed 14 test(s), exit 0 - feat: add opt-in native link pill presentation #862's 13 tests plus testHarnessRuns, discovered and run with no project change.
  • Failure reporting: testHarnessRuns broken deliberately, confirmed the assertion text above reaches the log and the lane exits non-zero.
  • Generator in isolation: zero files, two files, and a rerun with no changes leaving the header untouched.

Not yet verified: CI pins Xcode 26.3 and these runs were on 27. xcresulttool get test-results summary exists on both, but the lane has not been observed green on 26.3 - that is what the checks on this PR will establish.

#862 needs a one-line fix of its own

ENRMLinkPillTextStorageTests.mm:255:

XCTAssertNotEqual(bitmap, NULL);    // CGContextRef vs __null

In Objective-C++ NULL is __null (a long), so the macro expands to a pointer/integer comparison - a hard error, not a warning. Already raised on #862.

This PR does not fix it and cannot - the file does not exist on this branch. What it changes is that the error is now enforced instead of ignored: before, #862 could merge with a broken test file and nothing would notice, because the suite was never compiled. Once this lands, #862's own CI goes red until the line is fixed.

Created with usage of AI tools

The EnrichedMarkdownExampleTests target introduced in #877 only searched
core/cpp/*, ios/internals and ios/input/internals. Any real suite importing
library headers by relative path fails to build, because those headers import
their siblings unqualified: ENRMLinkPillAttachment.h imports "ENRMUIKit.h",
which lives in ios/utils and was unreachable.

The placeholder smoke test imports nothing from the library, so the lane was
green without ever proving a header could be resolved.

vendor/ and generated/ stay out: sweeping vendor in pulls three RaTeX
xcframework module maps into scope and clang rejects the duplicate RaTeXFFI
module definitions.

Created with usage of AI tools
EnrichedMarkdownExample.xcodeproj is objectVersion 54 and lists sources
explicitly, so nothing under __tests__/ios is discovered automatically. #877
said the real suites "plug into the same target on merge"; that holds for
Android, where Gradle auto-discovers src/test/java, but on iOS each file needs
a PBXFileReference, a PBXBuildFile, a group entry and a Sources phase entry.

Adds ENRMLinkPillTextStorageTests.mm from #862. With this plus the header
search paths, the lane runs 13 tests instead of the placeholder alone.

This branch is red until #862 merges: the .mm it references does not exist on
main yet. It also needs the one-line NULL -> nullptr fix on #862 itself
(ENRMLinkPillTextStorageTests.mm:254) -- XCTAssertNotEqual(bitmap, NULL)
compares CGContextRef against __null, which is a hard error in ObjC++.

Created with usage of AI tools
Replaces the per-file pbxproj registration added in the previous commit.
EnrichedMarkdownExample.xcodeproj is objectVersion 54 and lists sources
explicitly, so every new __tests__/ios file needed four pbxproj entries,
and a file nobody wired stayed silently unrun - which is how the lane
passed while covering nothing.

A "Generate library test sources" phase on the test target now globs
__tests__/ios and emits an include list that one wired umbrella source,
ENRMLibraryTests.mm, pulls in. Adding a test file there needs no project
change, matching what Gradle already does for src/test/java on Android.

test-ios.sh additionally counts the "- (void)test" prototypes present and
fails when fewer tests than that ran, so a suite dropping out of the run
cannot leave the lane green.

Created with usage of AI tools
@eszlamczyk
eszlamczyk marked this pull request as ready for review September 29, 2026 06:05
@eszlamczyk
eszlamczyk merged commit bea6652 into main Oct 9, 2026
14 checks passed
@eszlamczyk
eszlamczyk deleted the fix/wire-ios-pill-tests branch October 9, 2026 06:53
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.

1 participant