Skip to content

Commit 3ea5dfd

Browse files
mikepitreclaude
andcommitted
test(expo): drive the expo-native fixture from verify launch inputs
A launch with a verify input renders a host with one home that shows whether a user is signed in and links to the screens the end-to-end tests need. A launch with none renders the fixture as before. The fixture had no Google client ID, so useSignInWithGoogle stopped in JS and never called the @clerk/expo-google-signin native module. app.json now lists the @clerk/expo-google-signin config plugin and three placeholder values under extra: a web client ID, an iOS client ID, and the iOS URL scheme the plugin registers. None belongs to a Google project, so nobody can sign in with them. They let the hook configure the native module and open the native Google sign-in. The Google button now shows how the flow ended: cancelled, a session, or the error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 3236baa commit 3ea5dfd

27 files changed

Lines changed: 1073 additions & 13 deletions

‎.changeset/large-aliens-arrive.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

‎integration/templates/expo-native/App.tsx‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,22 @@ import { ClerkProvider, useAuth, useUser } from '@clerk/expo';
22
import { AuthView, UserButton, UserProfileView } from '@clerk/expo/native';
33
import { tokenCache } from '@clerk/expo/token-cache';
44
import { useState } from 'react';
5-
import { Button, Modal, StyleSheet, Text, View } from 'react-native';
5+
import { Button, LogBox, Modal, StyleSheet, Text, View } from 'react-native';
66

77
import { BiometricAvailabilityButton } from './components/BiometricAvailabilityButton';
88
import { GoogleSignInButton } from './components/GoogleSignInButton';
9+
import { readVerifyLaunch } from './verify/launch';
10+
import { logRequests, VerifyHost } from './verify/VerifyHost';
911

1012
const publishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY;
13+
const verifyLaunch = readVerifyLaunch();
1114

12-
if (!publishableKey) {
15+
if (verifyLaunch) {
16+
LogBox.ignoreAllLogs();
17+
if (verifyLaunch.debugLogging) {
18+
logRequests();
19+
}
20+
} else if (!publishableKey) {
1321
throw new Error('Missing EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY');
1422
}
1523

@@ -111,6 +119,10 @@ function NativeBuildFixture() {
111119
}
112120

113121
export default function App() {
122+
if (verifyLaunch) {
123+
return <VerifyHost launch={verifyLaunch} />;
124+
}
125+
114126
return (
115127
<ClerkProvider
116128
publishableKey={publishableKey}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const { version: expoVersion } = require('expo/package.json');
2+
3+
module.exports = ({ config }) =>
4+
expoVersion.startsWith('57.')
5+
? { ...config, plugins: [...config.plugins, ['expo-build-properties', { ios: { enableSceneSupport: true } }]] }
6+
: config;

‎integration/templates/expo-native/app.json‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
"android": {
1313
"package": "com.clerk.exponativebuildfixture"
1414
},
15-
"plugins": ["expo-secure-store", "@clerk/expo", "expo-web-browser"]
15+
"plugins": ["expo-secure-store", "@clerk/expo", "@clerk/expo-google-signin", "expo-web-browser"],
16+
"extra": {
17+
"EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID": "000000000000-clerkverifyplaceholderweb.apps.googleusercontent.com",
18+
"EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID": "000000000000-clerkverifyplaceholderios.apps.googleusercontent.com",
19+
"EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME": "com.googleusercontent.apps.000000000000-clerkverifyplaceholderios"
20+
}
1621
}
1722
}

‎integration/templates/expo-native/components/GoogleSignInButton.tsx‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@ export function GoogleSignInButton() {
1212
testID='google-sign-in-button'
1313
title='Sign in with Google'
1414
onPress={() => {
15-
void startGoogleAuthenticationFlow().catch((error: unknown) => {
16-
const message = error instanceof Error ? error.message : String(error);
17-
setResult(message.replace(/\s+/g, ' '));
18-
});
15+
setResult(null);
16+
void startGoogleAuthenticationFlow().then(
17+
({ createdSessionId }) =>
18+
setResult(createdSessionId ? 'Google sign-in created a session' : 'Google sign-in was cancelled'),
19+
(error: unknown) => {
20+
const message = error instanceof Error ? error.message : String(error);
21+
setResult(`Google sign-in failed: ${message.replace(/\s+/g, ' ')}`);
22+
},
23+
);
1924
}}
2025
/>
2126
{result && <Text testID='google-result'>{result}</Text>}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const fs = require('node:fs');
2+
const path = require('node:path');
3+
const { getDefaultConfig } = require('expo/metro-config');
4+
const { dependencies, devDependencies } = require('./package.json');
5+
6+
const config = getDefaultConfig(__dirname);
7+
const monorepoRoot = path.resolve(__dirname, '../../..');
8+
const fixtureEntry = path.join(__dirname, 'index.js');
9+
const fixturePackages = new Set(Object.keys({ ...dependencies, ...devDependencies }));
10+
11+
function linksToWorkspace() {
12+
try {
13+
return (
14+
fs.realpathSync(path.join(__dirname, 'node_modules/@clerk/expo')) === path.join(monorepoRoot, 'packages/expo')
15+
);
16+
} catch {
17+
return false;
18+
}
19+
}
20+
21+
function packageName(moduleName) {
22+
const [scope, name] = moduleName.split('/');
23+
return scope.startsWith('@') ? `${scope}/${name}` : scope;
24+
}
25+
26+
if (linksToWorkspace()) {
27+
config.watchFolders = [monorepoRoot];
28+
config.resolver.resolveRequest = (context, moduleName, platform) => {
29+
const fromWorkspace = !context.originModulePath.startsWith(__dirname + path.sep);
30+
if (fromWorkspace && fixturePackages.has(packageName(moduleName))) {
31+
return context.resolveRequest({ ...context, originModulePath: fixtureEntry }, moduleName, platform);
32+
}
33+
return context.resolveRequest(context, moduleName, platform);
34+
};
35+
}
36+
37+
module.exports = config;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
plugins {
2+
id 'com.android.library'
3+
id 'expo-module-gradle-plugin'
4+
}
5+
6+
group = 'expo.modules.verifylaunchconfig'
7+
version = '1.0.0'
8+
9+
android {
10+
namespace "expo.modules.verifylaunchconfig"
11+
defaultConfig {
12+
versionCode 1
13+
versionName "1.0.0"
14+
}
15+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<manifest>
2+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package expo.modules.verifylaunchconfig
2+
3+
import android.content.Context
4+
import expo.modules.kotlin.modules.Module
5+
import expo.modules.kotlin.modules.ModuleDefinition
6+
7+
private const val PREFERENCES = "verify_launch_config"
8+
private const val STORAGE_SCOPE_KEY = "storageScope"
9+
private val CLERK_STORAGE = listOf("clerk_preferences", "SecureStore")
10+
11+
class VerifyLaunchConfigModule : Module() {
12+
override fun definition() = ModuleDefinition {
13+
Name("VerifyLaunchConfig")
14+
15+
Function("readLaunchInputs") {
16+
val extras = appContext.currentActivity?.intent?.extras ?: return@Function emptyMap<String, String>()
17+
extras.keySet()
18+
.filter { it.startsWith("verify") }
19+
.mapNotNull { key -> extras.getString(key)?.let { key to it } }
20+
.toMap()
21+
}
22+
23+
Function("applyStorageScope") { scope: String ->
24+
val context = appContext.reactContext ?: return@Function
25+
val preferences = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE)
26+
if (preferences.getString(STORAGE_SCOPE_KEY, null) == scope) {
27+
return@Function
28+
}
29+
30+
CLERK_STORAGE.forEach { context.deleteSharedPreferences(it) }
31+
preferences.edit().putString(STORAGE_SCOPE_KEY, scope).commit()
32+
}
33+
}
34+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"platforms": ["apple", "android"],
3+
"apple": {
4+
"modules": ["VerifyLaunchConfigModule"]
5+
},
6+
"android": {
7+
"modules": ["expo.modules.verifylaunchconfig.VerifyLaunchConfigModule"]
8+
}
9+
}

0 commit comments

Comments
 (0)