Develop, test & publish
The package lives in native/ beside the web package; native/example is the Expo playground for iOS, Android, and the web.
cd nativenpm installnpm run example # Expo dev server: w (web), a (Android), i (iOS)npm run check # publish/ freshness, TypeScript, unit + component testsnpm run test:e2e # Playwright: UI suites + real SIP/WebRTC calls on a local registrarnpm run test:live # Real JsSIP registration against your PBXnpm run test:live:call # A real call through your PBX from the web build (opt-in)npm run publish:check # Stage and dry-run the JSR packageTest layers
Section titled “Test layers”| Layer | Tool | Covers |
|---|---|---|
| Core | Jest + mocked JsSIP and media adapters | Lifecycle races, permission states, DTMF fallback, speaker/audio session, stats levels, tones, validation |
| Components | React Native Testing Library | Every surface and preset, keypad, incoming, slide-to-answer, transfer, settings, island behaviours, a11y names |
| Browser | Playwright (desktop + Pixel 7) | The real web build: flows, island bounds, theme/preset switching, axe WCAG A/AA, reduced motion, orb motion |
| SIP wire | Playwright + local registrar | Two phones register with digest auth and call each other with real JsSIP and WebRTC: answer, live audio levels, mute, hold/remote hold, DTMF, hangup, rejected passwords, unknown numbers |
| Live PBX | Jest (Node WebSocket) / Playwright | Real REGISTER against your server; with SIP_TEST_TARGET, a real call with Chromium’s fake microphone |
Live tests read native/.env.local (git-ignored; see .env.example) or environment variables, and skip without credentials. In GitHub Actions, add SIP_WSS_URL, SIP_URI, SIP_USERNAME, SIP_PASSWORD, and SIP_TEST_TARGET as repository secrets then run the React Native workflow manually (workflow_dispatch) or add the live-sip label to a pull request: the live job registers and places a controlled call against your PBX. Ordinary pushes and pull request updates never use the account. Secrets are not available to pull requests from forks. The live browser call only runs with SIP_LIVE=1 (npm run test:live:call) so routine runs never touch a real account.
Native builds
Section titled “Native builds”cd native/examplenpx expo prebuildnpx expo run:android # needs JDK 17 and the Android SDKnpx expo run:ios # needs XcodePublishing to JSR
Section titled “Publishing to JSR”- Edit
native/src, thennpm run package:buildto regenerate the JSX-freepublish/tree (CI fails on a stale tree). - Keep
native/package.jsonandnative/jsr.jsonversions aligned; JSR versions are immutable. - The Publish workflow stages the package with only its runtime dependencies (
npm run publish:stage) and runsnpx jsr publishfrom.jsr-stageusing GitHub OIDC. Link@azeer-ui-widget/react-native-phoneto this repository in the JSR package settings first.
Staging matters: publishing from native/ would make Deno resolve the Expo example workspace. The staged manifest uses wide ranges (react-native: 0.x, react-native-webrtc: 124 - 999) so npm consumers never get a second React Native nested inside the package.