diff --git a/.github/workflows/onPush.yaml b/.github/workflows/onPush.yaml index 862b693..272272c 100644 --- a/.github/workflows/onPush.yaml +++ b/.github/workflows/onPush.yaml @@ -29,6 +29,18 @@ jobs: run: | npm install -g license-checker license-checker --failOn "AGPL-1.0-only;AGPL-1.0-or-later;AGPL-3.0-only;AGPL-3.0-or-later;GPL-1.0-only;GPL-1.0-or-later;GPL-2.0-only;GPL-2.0-or-later;GPL-3.0-only;GPL-3.0-or-later;LGPL-2.0-only;LGPL-2.0-or-later;LGPL-2.1-only;LGPL-2.1-or-later;LGPL-3.0-only;LGPL-3.0-or-later;LGPLLR;MPL-1.1" + test: + name: 'Unit Tests' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup + uses: ./.github/actions/setup + + - name: Run unit tests + run: yarn test build: name: 'Build' runs-on: ubuntu-latest diff --git a/android/src/main/java/com/frontegg/reactnative/FronteggRNModule.kt b/android/src/main/java/com/frontegg/reactnative/FronteggRNModule.kt index 3155800..a88993d 100644 --- a/android/src/main/java/com/frontegg/reactnative/FronteggRNModule.kt +++ b/android/src/main/java/com/frontegg/reactnative/FronteggRNModule.kt @@ -7,6 +7,7 @@ import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableMap import com.facebook.react.common.LifecycleState import com.facebook.react.modules.core.DeviceEventManagerModule @@ -137,8 +138,22 @@ class FronteggRNModule(val reactContext: ReactApplicationContext) : } @ReactMethod - fun directLoginAction(type: String, data: String, ephemeralSession: Boolean, promise: Promise) { + fun directLoginAction( + type: String, + data: String, + ephemeralSession: Boolean, + additionalQueryParams: ReadableMap?, + promise: Promise + ) { val activity = reactApplicationContext.currentActivity + // Parity note: the JS `directLoginAction(type, data, ephemeralSession, additionalQueryParams)` + // signature is shared across platforms, so both trailing args must be declared here to keep the + // JS↔native argument mapping aligned (otherwise `additionalQueryParams` collides with the + // Promise slot). iOS honors both (see ios/FronteggRN.swift), but the native Android SDK's + // `directLoginAction(activity, type, data)` does not yet accept them — threading them through + // requires native support in frontegg-android-kotlin. Until then they are accepted no-ops on + // Android. `ephemeralSession` is inherently iOS-only here (Android runs the flow in the embedded + // WebView, not an ASWebAuthenticationSession-style browser session). auth.directLoginAction(activity!!, type, data) promise.resolve(true) } diff --git a/src/FronteggNative.ts b/src/FronteggNative.ts index 1970eba..8783ee2 100644 --- a/src/FronteggNative.ts +++ b/src/FronteggNative.ts @@ -43,6 +43,14 @@ export async function refreshToken() { return FronteggRN.refreshToken(); } +/** + * Starts a direct login action (e.g. a specific social provider) in the embedded login flow. + * + * Note on `ephemeralSession` / `additionalQueryParams`: these are currently honored on **iOS + * only**. On Android the underlying native SDK's `directLoginAction` does not yet accept them, + * so they are ignored there (tracked upstream in `frontegg-android-kotlin`). `ephemeralSession` + * is inherently iOS-specific (it maps to the ASWebAuthenticationSession browser session). + */ export async function directLoginAction( type: string, data: string, diff --git a/src/__tests__/index.test.tsx b/src/__tests__/index.test.tsx index 9b51ace..7c85ece 100644 --- a/src/__tests__/index.test.tsx +++ b/src/__tests__/index.test.tsx @@ -1,10 +1,11 @@ import { NativeModules } from 'react-native'; -import { openAdminPortal } from '../FronteggNative'; +import { directLoginAction, openAdminPortal } from '../FronteggNative'; jest.mock('react-native', () => ({ NativeModules: { FronteggRN: { openAdminPortal: jest.fn(() => Promise.resolve(null)), + directLoginAction: jest.fn(() => Promise.resolve()), subscribe: jest.fn(), }, }, @@ -22,3 +23,30 @@ describe('openAdminPortal', () => { expect(NativeModules.FronteggRN.openAdminPortal).toHaveBeenCalled(); }); }); + +describe('directLoginAction', () => { + beforeEach(() => { + (NativeModules.FronteggRN.directLoginAction as jest.Mock).mockClear(); + }); + + it('bridges type, data, ephemeralSession and additionalQueryParams to the native module', async () => { + const params = { prompt: 'consent', foo: 'bar' }; + await directLoginAction('social-login', 'google', false, params); + expect(NativeModules.FronteggRN.directLoginAction).toHaveBeenCalledWith( + 'social-login', + 'google', + false, + params + ); + }); + + it('defaults ephemeralSession to true and forwards an undefined additionalQueryParams', async () => { + await directLoginAction('social-login', 'google'); + expect(NativeModules.FronteggRN.directLoginAction).toHaveBeenCalledWith( + 'social-login', + 'google', + true, + undefined + ); + }); +});