From 5bffe9fc4c0257b4e2e9f1954470d5dc989a6614 Mon Sep 17 00:00:00 2001 From: Wes Risenmay Date: Tue, 20 Jan 2026 13:36:50 -0700 Subject: [PATCH 1/2] converted to expo router, example app now navigates back to the app after a successful connection, added an e2e test --- example/app/App.tsx | 166 ----------------------------- example/app/_layout.tsx | 59 +++++++++- example/app/budgets.tsx | 22 ++++ example/app/connect.tsx | 49 +++++++++ example/app/goals.tsx | 22 ++++ example/app/index.tsx | 47 +++++++- example/app/oauth_complete.tsx | 13 +++ example/app/pulse.tsx | 22 ++++ example/app/spending.tsx | 22 ++++ example/app/transactions.tsx | 22 ++++ example/maestro/connectWidget.yaml | 22 +++- 11 files changed, 294 insertions(+), 172 deletions(-) delete mode 100644 example/app/App.tsx create mode 100644 example/app/budgets.tsx create mode 100644 example/app/connect.tsx create mode 100644 example/app/goals.tsx create mode 100644 example/app/oauth_complete.tsx create mode 100644 example/app/pulse.tsx create mode 100644 example/app/spending.tsx create mode 100644 example/app/transactions.tsx diff --git a/example/app/App.tsx b/example/app/App.tsx deleted file mode 100644 index e9db4cd..0000000 --- a/example/app/App.tsx +++ /dev/null @@ -1,166 +0,0 @@ -import React, { FC, PropsWithChildren } from "react" -import { SafeAreaView, Text, View, StyleSheet, StyleProp, ViewStyle, Platform } from "react-native" -import { NativeRouter, Routes, Route, Link } from "react-router-native" - -import { - BudgetsWidget, - ConnectWidget, - GoalsWidget, - PulseWidget, - SpendingWidget, - TransactionsWidget, -} from "@mxenabled/react-native-widget-sdk" - -const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" -const proxy = `${baseUrl}/user/widget_urls` -const styles = StyleSheet.create({ - page: { - backgroundColor: "#ffffff", - paddingTop: 10, - }, - heading: { - paddingLeft: 25, - }, - navigation: { - flex: 1, - flexWrap: "wrap", - padding: 10, - }, - navigationButton: { - alignItems: "center", - backgroundColor: "#fbfbfb", - margin: 10, - padding: 20, - }, - navItemNormalText: { - color: "#2980b9", - fontSize: 20, - }, - back: { - color: "#2980b9", - fontSize: 15, - marginTop: 10, - marginBottom: 10, - }, -}) - -export default function App() { - return ( - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - ) -} - -const Home = () => ( - - Connect - Budgets - Goals - Pulse - Spending - Transactions - -) - -const Page: FC; goBack?: boolean }>> = ({ - children, - goBack = true, -}) => ( - - {goBack ? ( - - - Back - - - ) : null} - {children} - -) - -const NavigationButton: FC> = ({ to, children }) => ( - - {children} - -) - -const Connect = () => { - return ( - - { - console.error(`SSO URL load error: ${error}`) - }} - onMessage={(url) => { - console.log(`Got a message: ${url}`) - }} - onInvalidMessageError={(url, _error) => { - console.log(`Got an unknown message: ${url}`) - }} - onLoad={(_payload) => { - console.log("Widget is loading") - }} - onLoaded={(_payload) => { - console.log("Widget has loaded") - }} - onStepChange={(payload) => { - console.log(`Moving from ${payload.previous} to ${payload.current}`) - }} - onSelectedInstitution={(payload) => { - console.log(`Selecting ${payload.name}`) - }} - /> - - ) -} - -const Budgets = () => { - return ( - - - - ) -} - -const Goals = () => { - return ( - - - - ) -} - -const Pulse = () => { - return ( - - - - ) -} - -const Spending = () => { - return ( - - - - ) -} - -const Transactions = () => { - return ( - - - - ) -} diff --git a/example/app/_layout.tsx b/example/app/_layout.tsx index d2a8b0b..1eea18e 100644 --- a/example/app/_layout.tsx +++ b/example/app/_layout.tsx @@ -1,5 +1,60 @@ -import { Stack } from "expo-router"; +import { Stack } from "expo-router" export default function RootLayout() { - return ; + return ( + + + + + + + + + + ) } diff --git a/example/app/budgets.tsx b/example/app/budgets.tsx new file mode 100644 index 0000000..268ddeb --- /dev/null +++ b/example/app/budgets.tsx @@ -0,0 +1,22 @@ +import React from "react" +import { SafeAreaView, StyleSheet, Platform } from "react-native" + +import { BudgetsWidget } from "@mxenabled/react-native-widget-sdk" + +const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" +const proxy = `${baseUrl}/user/widget_urls` + +const styles = StyleSheet.create({ + page: { + backgroundColor: "#ffffff", + paddingTop: 10, + }, +}) + +export default function Budgets() { + return ( + + + + ) +} diff --git a/example/app/connect.tsx b/example/app/connect.tsx new file mode 100644 index 0000000..4f88fbc --- /dev/null +++ b/example/app/connect.tsx @@ -0,0 +1,49 @@ +import React from "react" +import { SafeAreaView, StyleSheet, Platform } from "react-native" +import * as Linking from "expo-linking" + +import { ConnectWidget } from "@mxenabled/react-native-widget-sdk" + +const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" +const proxy = `${baseUrl}/user/widget_urls` + +const styles = StyleSheet.create({ + page: { + backgroundColor: "#ffffff", + paddingTop: 10, + }, +}) + +export default function Connect() { + const clientRedirectUrl = Linking.createURL("connect") + + return ( + + { + console.error(`SSO URL load error: ${error}`) + }} + onMessage={(url) => { + console.log(`Got a message: ${url}`) + }} + onInvalidMessageError={(url, _error) => { + console.log(`Got an unknown message: ${url}`) + }} + onLoad={(_payload) => { + console.log("Widget is loading") + }} + onLoaded={(_payload) => { + console.log("Widget has loaded") + }} + onStepChange={(payload) => { + console.log(`Moving from ${payload.previous} to ${payload.current}`) + }} + onSelectedInstitution={(payload) => { + console.log(`Selecting ${payload.name}`) + }} + /> + + ) +} diff --git a/example/app/goals.tsx b/example/app/goals.tsx new file mode 100644 index 0000000..2745921 --- /dev/null +++ b/example/app/goals.tsx @@ -0,0 +1,22 @@ +import React from "react" +import { SafeAreaView, StyleSheet, Platform } from "react-native" + +import { GoalsWidget } from "@mxenabled/react-native-widget-sdk" + +const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" +const proxy = `${baseUrl}/user/widget_urls` + +const styles = StyleSheet.create({ + page: { + backgroundColor: "#ffffff", + paddingTop: 10, + }, +}) + +export default function Goals() { + return ( + + + + ) +} diff --git a/example/app/index.tsx b/example/app/index.tsx index f61d7ac..00b0235 100644 --- a/example/app/index.tsx +++ b/example/app/index.tsx @@ -1,5 +1,46 @@ -import App from "./App" +import React, { FC, PropsWithChildren } from "react" +import { SafeAreaView, Text, View, StyleSheet } from "react-native" +import { Link } from "expo-router" -export default function Index() { - return +const styles = StyleSheet.create({ + page: { + backgroundColor: "#ffffff", + paddingTop: 10, + }, + navigation: { + display: "flex", + height: "100%", + gap: 10, + margin: 50, + }, + navigationButton: { + alignItems: "center", + backgroundColor: "rgb(144, 202, 249)", + padding: 20, + }, + navItemNormalText: { + color: "rgba(0, 0, 0, 0.87)", + fontSize: 20, + }, +}) + +export default function Home() { + return ( + + + Connect + Budgets + Goals + Pulse + Spending + Transactions + + + ) } + +const NavigationButton: FC> = ({ href, children }) => ( + + {children} + +) diff --git a/example/app/oauth_complete.tsx b/example/app/oauth_complete.tsx new file mode 100644 index 0000000..7b230f7 --- /dev/null +++ b/example/app/oauth_complete.tsx @@ -0,0 +1,13 @@ +import { useEffect } from "react" +import { useLocalSearchParams } from "expo-router" + +export default function OAuthComplete() { + const params = useLocalSearchParams() + + useEffect(() => { + // Handle the OAuth redirect + console.log("OAuth redirect received", params) + }, [params]) + + return null +} diff --git a/example/app/pulse.tsx b/example/app/pulse.tsx new file mode 100644 index 0000000..76593df --- /dev/null +++ b/example/app/pulse.tsx @@ -0,0 +1,22 @@ +import React from "react" +import { SafeAreaView, StyleSheet, Platform } from "react-native" + +import { PulseWidget } from "@mxenabled/react-native-widget-sdk" + +const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" +const proxy = `${baseUrl}/user/widget_urls` + +const styles = StyleSheet.create({ + page: { + backgroundColor: "#ffffff", + paddingTop: 10, + }, +}) + +export default function Pulse() { + return ( + + + + ) +} diff --git a/example/app/spending.tsx b/example/app/spending.tsx new file mode 100644 index 0000000..791733f --- /dev/null +++ b/example/app/spending.tsx @@ -0,0 +1,22 @@ +import React from "react" +import { SafeAreaView, StyleSheet, Platform } from "react-native" + +import { SpendingWidget } from "@mxenabled/react-native-widget-sdk" + +const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" +const proxy = `${baseUrl}/user/widget_urls` + +const styles = StyleSheet.create({ + page: { + backgroundColor: "#ffffff", + paddingTop: 10, + }, +}) + +export default function Spending() { + return ( + + + + ) +} diff --git a/example/app/transactions.tsx b/example/app/transactions.tsx new file mode 100644 index 0000000..490d01f --- /dev/null +++ b/example/app/transactions.tsx @@ -0,0 +1,22 @@ +import React from "react" +import { SafeAreaView, StyleSheet, Platform } from "react-native" + +import { TransactionsWidget } from "@mxenabled/react-native-widget-sdk" + +const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" +const proxy = `${baseUrl}/user/widget_urls` + +const styles = StyleSheet.create({ + page: { + backgroundColor: "#ffffff", + paddingTop: 10, + }, +}) + +export default function Transactions() { + return ( + + + + ) +} diff --git a/example/maestro/connectWidget.yaml b/example/maestro/connectWidget.yaml index 18a6d60..bb9f7ba 100644 --- a/example/maestro/connectWidget.yaml +++ b/example/maestro/connectWidget.yaml @@ -1,8 +1,28 @@ appId: com.anonymous.example --- - retry: - maxRetries: 2 + maxRetries: 3 commands: - launchApp + - assertVisible: "Connect" - tapOn: "Connect" - assertVisible: "Select your institution" + - tapOn: ".*(OAuth).*" + - tapOn: "Go to log in" + - assertVisible: "Authorization required" + - assertVisible: "Authorize" + - runFlow: + when: + platform: android + commands: + - tapOn: + text: "Authorize" + - runFlow: + when: + platform: iOS + commands: + - tapOn: + text: "Authorize" + index: 1 + - tapOn: "Open" + - assertVisible: "Done" From f9539e9cae5d25c712143aba60e07b0e59811cf0 Mon Sep 17 00:00:00 2001 From: Wes Risenmay Date: Tue, 20 Jan 2026 13:41:20 -0700 Subject: [PATCH 2/2] audit fix, tick version and update changelog --- CHANGELOG.md | 10 ++++++++++ example/package-lock.json | 12 ++++++------ package-lock.json | 13 +++++++------ package.json | 2 +- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e9c67..aa83933 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +## [2.0.3] + +### Added + +- An E2E test for the app redirect + +### Fixed + +- Redirect back to the example app after a successful connection + ## [2.0.2] ### Added diff --git a/example/package-lock.json b/example/package-lock.json index 3b37040..a47296d 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -11704,9 +11704,9 @@ } }, "node_modules/tar": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", - "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.4.tgz", + "integrity": "sha512-AN04xbWGrSTDmVwlI4/GTlIIwMFk/XEv7uL8aa57zuvRy6s4hdBed+lVq2fAZ89XDa7Us3ANXcE3Tvqvja1kTA==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -12137,9 +12137,9 @@ } }, "node_modules/undici": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz", - "integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", "license": "MIT", "engines": { "node": ">=18.17" diff --git a/package-lock.json b/package-lock.json index 5af19bb..d51332a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2754,9 +2754,9 @@ } }, "node_modules/@microsoft/api-extractor/node_modules/diff": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", - "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -6233,10 +6233,11 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } diff --git a/package.json b/package.json index a556a16..1aec094 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@mxenabled/react-native-widget-sdk", "description": "MX React Native Widget SDK", - "version": "2.0.2", + "version": "2.0.3", "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts",