Skip to content

Commit

Permalink
Merge branch 'main' into storybook2
Browse files Browse the repository at this point in the history
  • Loading branch information
mraible authored Jun 10, 2024
2 parents cfcdd5c + a1f3516 commit 22ac34d
Show file tree
Hide file tree
Showing 11 changed files with 678 additions and 38 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ jobs:
if: steps.compare.outputs.equals != 'true'
working-directory: ${{ github.workspace }}/backend
- run: npm run e2e:test:ios
timeout-minutes: 15
env:
DETOX_RECORD_VIDEOS: "${{ contains(github.event.pull_request.labels.*.name, 'pr: record-videos') && 'all' || 'none' }}"
if: steps.compare.outputs.equals != 'true'
- uses: actions/upload-artifact@v4
if: steps.compare.outputs.equals != 'true' && always()
Expand Down
4 changes: 4 additions & 0 deletions generators/react-native/generator.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ export default class extends BaseApplicationGenerator {
preparingPatchInFile() {
this.patchInFile = patchInFile.bind(this);
},
husky({ application }) {
application.nodeDependencies.husky = '9.0.11';
application.nodeDependencies['lint-staged'] = '15.2.5';
},
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,12 @@
import { Platform } from 'react-native';
import { DiscoveryDocument, makeRedirectUri } from 'expo-auth-session';
import { AuthRequest, AuthSessionResult, DiscoveryDocument, makeRedirectUri } from 'expo-auth-session';
import * as AuthSession from 'expo-auth-session';
import { generateHexStringAsync, buildCodeAsync } from 'expo-auth-session/src/PKCE';
import { buildQueryString } from 'expo-auth-session/src/QueryParams';
import * as WebBrowser from 'expo-web-browser';
import * as Linking from 'expo-linking';
import AppConfig from '../../config/app-config';

WebBrowser.maybeCompleteAuthSession();

interface AuthParams {
state: string;
codeVerifier: string;
authUrl: string;
}

export async function getAuthParams(clientId: string, redirectUri: string, discovery: DiscoveryDocument): Promise<AuthParams> {
const state = await generateHexStringAsync(16);
const { codeVerifier, codeChallenge } = await buildCodeAsync();
const authenticationOptions = {
response_type: 'code',
code_challenge: codeChallenge,
code_challenge_method: 'S256',
scope: ['openid', 'profile', 'email', 'address', 'phone', 'offline_access'].join(' '),
state,
client_id: clientId,
redirect_uri: redirectUri,
audience: 'api://default',
};
const authUrl = `${discovery.discoveryDocument?.authorization_endpoint}?${buildQueryString(authenticationOptions)}`;
return {
state,
codeVerifier,
authUrl,
};
}

export async function exchangeCodeForToken(
clientId: string,
redirectUri: string,
Expand Down Expand Up @@ -63,24 +34,40 @@ export function extractCodeOrThrow(result: AuthSession.AuthSessionResult, state:
throw result;
}
}

export async function getDiscovery(issuer: string): Promise<DiscoveryDocument> {
return AuthSession.fetchDiscoveryAsync(issuer);
}

export function generateShortUUID() {
return Math.random().toString(36).substring(2, 15);
}

export async function doOauthPkceFlow(clientId: string, issuer: string): Promise<AuthSession.TokenResponse> {
// set up redirect uri
const redirectUri = makeRedirectUri({ useProxy: AppConfig.useExpoAuthProxy });
// fetch oauth issuer information from discovery endpoint
const discovery = await getDiscovery(issuer);
// set up the IDP url, prepare codeVerifier and state
const { authUrl, codeVerifier, state } = await getAuthParams(clientId, redirectUri, discovery);
// redirect to the IDP
const returnUri = Platform.OS === 'android' && !AppConfig.useExpoAuthProxy ? redirectUri : Linking.createURL('/');
const authResult = await AuthSession.startAsync({ authUrl, returnUrl: returnUri });
const state = generateShortUUID();
// Get Authorization code
const authRequestOptions: AuthSession.AuthRequestConfig = {
responseType: AuthSession.ResponseType.Code,
clientId,
redirectUri: returnUri,
prompt: AuthSession.Prompt.Login,
scopes: ['openid', 'profile', 'email', 'address', 'phone', 'offline_access'],
state,
usePKCE: true,
};
const authRequest: AuthRequest = new AuthSession.AuthRequest(authRequestOptions);
const authResult: AuthSessionResult = await authRequest.promptAsync(discovery, { useProxy: AppConfig.useExpoAuthProxy });
// check the response for success/failure
const code = extractCodeOrThrow(authResult, state);
// exchange the received code for an access token
return exchangeCodeForToken(clientId, redirectUri, discovery, code, codeVerifier);
return exchangeCodeForToken(clientId, returnUri, discovery, code, authRequest.codeVerifier || '');
}

export async function logoutFromIdp(clientId: string, issuer: string, idToken: string) {
Expand All @@ -91,7 +78,10 @@ export async function logoutFromIdp(clientId: string, issuer: string, idToken: s
if (endSessionEndpoint) {
// set up redirect uri
const redirectUri = makeRedirectUri({ useProxy: AppConfig.useExpoAuthProxy });
await WebBrowser.openAuthSessionAsync(`${endSessionEndpoint}?id_token_hint=${idToken}&client_id=${clientId}&post_logout_redirect_uri=${redirectUri}`, redirectUri);
await WebBrowser.openAuthSessionAsync(
`${endSessionEndpoint}?id_token_hint=${idToken}&client_id=${clientId}&post_logout_redirect_uri=${redirectUri}`,
redirectUri,
);
} else if (issuer.includes('auth0.com')) {
// Auth0 need special handling since end_session_endpoint is not in oidc-configuration
const redirectUri = makeRedirectUri({ useProxy: AppConfig.useExpoAuthProxy });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<%_ if (anyFieldIsDateDerived) { _%>
const jestExpect = require('expect');
const { expect: jestExpect } = require('expect');
<%_ } _%>
const {
reloadApp,
Expand Down Expand Up @@ -51,7 +51,7 @@ Date.prototype.toCustomLocalDate = function() {
}
_%>

describe.skip('<%= entityNameCapitalized %> Screen Tests', () => {
describe<%- authenticationTypeOauth2 || anyFieldIsBlobDerived ? '.skip' : '' %>('<%= entityNameCapitalized %> Screen Tests', () => {
beforeEach(async () => {
await reloadApp();
await loginAsUser();
Expand Down
3 changes: 2 additions & 1 deletion generators/react-native/templates/e2e/utils.js.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const toggleSwitchToValue = async (switchId, targetValue) => {
const scrollTo = async (fieldId, listId, size = 0.15, direction = 'up', speed = 'slow') => {
await waitFor(element(by.id(fieldId)))
.toBeVisible()
.whileElement(by.type('ABI49_0_0RCTScrollView').withAncestor(by.id(listId)))
.whileElement(by.id(listId))
.scroll(500, 'down');
};

Expand Down Expand Up @@ -144,6 +144,7 @@ const reloadApp = async (bailOnFailure) => {
newInstance: true,
launchArgs: {
detoxEnableSynchronization: false,
permissions: { camera: 'YES', photo: 'YES' },
// Don't show developer menu.
EXKernelDisableNuxDefaultsKey: true,
},
Expand Down
Loading

0 comments on commit 22ac34d

Please sign in to comment.