Skip to content

test(ios): add XCTest unit-test infrastructure for native Swift modules#60

Open
alichherawalla wants to merge 1 commit into
mainfrom
test/ios-unit-test-infrastructure
Open

test(ios): add XCTest unit-test infrastructure for native Swift modules#60
alichherawalla wants to merge 1 commit into
mainfrom
test/ios-unit-test-infrastructure

Conversation

@alichherawalla
Copy link
Copy Markdown
Owner

Summary

  • Adds ios/OffgridMobileTests/OffgridMobileTests.swift with 12 XCTest cases covering all 3 custom native Swift modules (PDFExtractorModule, CoreMLDiffusionModule, DownloadManagerModule)
  • Adds OffgridMobileTests unit-test target to project.pbxproj with BUNDLE_LOADER/TEST_HOST wired to the app binary so @testable import OffgridMobile works
  • Adds target 'OffgridMobileTests' to Podfile with inherit! :search_paths so the test target picks up Pods header search paths without relinking
  • Adds .github/workflows/test-ios.yml CI workflow that runs on push/PR to main
  • Adds test:ios npm script for local runs

Test plan

  • xcodebuild build-for-testing compiles cleanly (0 errors)
  • All 12 tests pass: CoreMLDiffusionModuleTests (5), DownloadManagerModuleTests (1), PDFExtractorModuleTests (3 — resolve, truncate, reject invalid path)
  • No new lint errors introduced (npm run lint)
  • TypeScript clean (npx tsc --noEmit)
  • Pre-existing Jest suite unaffected

🤖 Generated with Claude Code

Set up the complete iOS unit-test target (OffgridMobileTests) to cover
the three custom native modules:

- PDFExtractorModuleTests: creates a PDF programmatically, asserts text
  extraction, truncation, and rejection of invalid paths
- CoreMLDiffusionModuleTests: verifies supportedEvents, isNpuSupported,
  isGenerating, isModelLoaded, cancelGeneration without a live bridge
- DownloadManagerModuleTests: verifies supportedEvents event names

Changes:
- ios/OffgridMobileTests/OffgridMobileTests.swift — 12 XCTest cases
- ios/OffgridMobile.xcodeproj/project.pbxproj — OffgridMobileTests
  target with BUNDLE_LOADER/TEST_HOST pointing at app binary
- ios/Podfile + Podfile.lock — test target inherits Pods search paths
- .github/workflows/test-ios.yml — CI workflow (push/PR to main)
- package.json — test:ios npm script

All 12 tests pass on the iPhone 16e simulator (iOS 26.2).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @alichherawalla, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the project's testing capabilities by integrating a comprehensive XCTest unit-test infrastructure for its native iOS Swift modules. This foundational work ensures the reliability and maintainability of critical native functionalities, while also streamlining the development workflow with new CI automation and local testing scripts. Additionally, it includes refactorings in the DownloadManagerScreen to improve code organization and prepare for future enhancements.

Highlights

  • iOS Unit Test Infrastructure: Introduced XCTest unit-test infrastructure for native Swift modules, including tests for PDFExtractorModule, CoreMLDiffusionModule, and DownloadManagerModule.
  • Xcode Project Configuration: Configured the Xcode project (project.pbxproj) to include the new OffgridMobileTests target, linking it to the main app binary for @testable import functionality.
  • CocoaPods Integration: Updated the Podfile to ensure the OffgridMobileTests target correctly inherits CocoaPods search paths.
  • CI Workflow Automation: Established a new CI workflow (.github/workflows/test-ios.yml) to automatically run iOS tests on push and pull requests to the main branch.
  • Local Testing Script: Added a 'test:ios' npm script for convenient local execution of the new iOS test suite.
  • Download Manager Refactoring: Refactored the DownloadManagerScreen component to improve code structure and testability, extracting status text logic and separating alert handling from asynchronous operations.
Changelog
  • tests/rntl/screens/DownloadManagerScreen.test.tsx
    • Added unit tests for the getStatusText helper function, covering 'running', 'pending', and 'paused' states.
  • ios/OffgridMobile.xcodeproj/project.pbxproj
    • Added new build file references for libPods-OffgridMobileTests.a and OffgridMobileTests.swift.
    • Introduced a PBXContainerItemProxy for the OffgridMobileTests target.
    • Added file references for OffgridMobileTests.swift, OffgridMobileTests.xctest, and Pods xcconfig files for the test target.
    • Included libPods-OffgridMobileTests.a in the Frameworks build phase.
    • Added OffgridMobileTests group and its Swift test file.
    • Defined a new PBXNativeTarget for OffgridMobileTests with its build phases and dependencies.
    • Updated PBXProject attributes to include TestTargetID for OffgridMobileTests.
    • Added OffgridMobileTests to the list of project targets.
    • Introduced a PBXResourcesBuildPhase for the test target.
    • Added a PBXShellScriptBuildPhase for [CP] Check Pods Manifest.lock for the test target.
    • Added a PBXTargetDependency for OffgridMobileTests on OffgridMobile.
    • Defined XCBuildConfiguration entries (Debug and Release) for OffgridMobileTests, setting BUNDLE_LOADER and TEST_HOST.
    • Created an XCConfigurationList for the OffgridMobileTests target.
  • ios/OffgridMobileTests/OffgridMobileTests.swift
    • Added a new Swift file containing XCTest cases for PDFExtractorModule (testing text extraction, truncation, and invalid paths), CoreMLDiffusionModule (testing supported events, NPU support, generation status, model loaded status, and cancellation), and DownloadManagerModule (testing supported events).
  • ios/Podfile
    • Added a new target 'OffgridMobileTests' block with inherit! :search_paths.
  • ios/Podfile.lock
    • Updated the PODFILE CHECKSUM to reflect changes in the Podfile.
  • package.json
    • Added a new npm script 'test:ios' to run iOS unit tests via xcodebuild.
  • src/screens/DownloadManagerScreen.tsx
    • Extracted download status text logic into a new getStatusText helper function.
    • Refactored handleRemoveDownload, handleDeleteModel, and handleDeleteImageModel to separate the asynchronous logic into new executeRemoveDownload, executeDeleteModel, and executeDeleteImageModel functions, respectively.
    • Updated isNaN checks to Number.isNaN.
    • Modified key generation for mapped items in activeItems and completedItems to be more robust.
    • Changed item.quantization && to !!item.quantization && for boolean evaluation.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/test-ios.yml
Activity
  • The pull request introduces new XCTest unit tests for native Swift modules and sets up the necessary infrastructure for running them.
  • A new CI workflow has been added to automate the execution of these iOS tests.
  • An npm script was created for local testing convenience.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot
7.1% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 20, 2026

Codecov Report

❌ Patch coverage is 96.42857% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.14%. Comparing base (65b4f41) to head (95c3e77).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/screens/DownloadManagerScreen.tsx 96.42% 0 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #60      +/-   ##
==========================================
+ Coverage   84.06%   84.14%   +0.07%     
==========================================
  Files          68       68              
  Lines        6415     6426      +11     
  Branches     1803     1802       -1     
==========================================
+ Hits         5393     5407      +14     
  Misses        649      649              
+ Partials      373      370       -3     
Files with missing lines Coverage Δ
src/screens/DownloadManagerScreen.tsx 91.37% <96.42%> (+2.42%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request successfully establishes the XCTest unit-testing infrastructure for native Swift modules on iOS. It includes a comprehensive set of test cases for the PDFExtractor, CoreMLDiffusion, and DownloadManager modules, along with the necessary Xcode project and Podfile configurations. Additionally, the PR refactors the DownloadManagerScreen in React Native to improve code organization and testability, and adds a CI workflow for automated iOS testing. The changes are well-structured and follow best practices for React Native and iOS development.

Comment on lines +174 to +178
setTimeout(() => {
void loadActiveDownloads().then(() => {
if (dlId) cancelledKeysRef.current.delete(capturedKey);
});
}, 1000);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using setTimeout to wait for native state synchronization is brittle and can lead to race conditions or unnecessary delays in the UI. If the native cancelBackgroundDownload method is asynchronous, it should ideally return a Promise that resolves only when the operation is fully reflected in the native state, or the UI should rely on event-driven updates from the native side.

let attrs: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 12)]
text.draw(in: CGRect(x: 72, y: 72, width: 468, height: 648), withAttributes: attrs)
}
try! data.write(to: url)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using try! in unit tests can lead to abrupt crashes without descriptive failure messages if the file system operation fails (e.g., due to permissions or disk space). It is generally safer to use XCTAssertNoThrow or a do-catch block with XCTFail to provide more context on failure.

}
)

waitForExpectations(timeout: 5)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The waitForExpectations(timeout:) method is part of an older XCTest API. For modern Swift code, especially when dealing with multiple expectations or more complex async flows, consider using the newer fulfillment(of:timeout:enforceOrder:) API introduced in iOS 16.0 / macOS 13.0.

if (upperName.includes(pattern)) return pattern;
}
const match = fileName.match(/[QqFf]\d+[_]?[KkMmSs]*/);
const match = /[QqFf]\d+_?[KkMmSs]*/.exec(fileName);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The regex /[QqFf]\d+_?[KkMmSs]*/ is executed using .exec(fileName). While functional, ensure that the regex is defined outside the function if it's called frequently to avoid recompiling the pattern on every execution, although modern engines optimize this well for literals.

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