From 2d00444a5a285560bc4aa613ae069a1b8c166ba7 Mon Sep 17 00:00:00 2001 From: "lalitdeore12@gmail.com" Date: Thu, 20 Feb 2025 18:45:05 +0530 Subject: [PATCH 01/32] [Feature] - Add frontend testing script for onprem --- .github/workflows/quick-testing.yml | 19 +- frontend/frontend-testing.sh | 104 ++++++++++ frontend/package.json | 3 +- frontend/selenium-test.js | 249 +++++++++++++++++++++++ frontend/src/App.jsx | 1 + frontend/src/components/EditWorkflow.jsx | 2 + frontend/src/views/LoginPage.jsx | 1 + frontend/src/views/Workflows2.jsx | 1 + 8 files changed, 377 insertions(+), 3 deletions(-) create mode 100644 frontend/frontend-testing.sh create mode 100644 frontend/selenium-test.js diff --git a/.github/workflows/quick-testing.yml b/.github/workflows/quick-testing.yml index 17ac28ac..db68f4ae 100644 --- a/.github/workflows/quick-testing.yml +++ b/.github/workflows/quick-testing.yml @@ -5,6 +5,7 @@ on: workflows: ["dockerbuild"] types: - completed + workflow_dispatch: jobs: build: @@ -17,9 +18,17 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 - + + - name: Set up Docker + uses: docker/setup-buildx-action@v1 + + - name: Install Docker Compose + run: | + sudo apt-get update + sudo apt-get install -y docker-compose + - name: Set up opensearch directory - run: mkdir shuffle-database && chmod -R 777 shuffle-database + run: mkdir -p shuffle-database && chmod -R 777 shuffle-database - name: Build the stack run: docker-compose up -d @@ -95,6 +104,12 @@ jobs: echo "User registration failed after $MAX_RETRIES attempts." exit 1 + - name: Run Selenium testing for frontend + run: | + cd $GITHUB_WORKSPACE/frontend + chmod +x frontend-testing.sh + ./frontend-testing.sh + - name: Get the API key and run a health check run: | RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search') diff --git a/frontend/frontend-testing.sh b/frontend/frontend-testing.sh new file mode 100644 index 00000000..454f9a12 --- /dev/null +++ b/frontend/frontend-testing.sh @@ -0,0 +1,104 @@ + +SERVER_URL="http://localhost:3000" + +declare -a Routes=() + +# Add all cloud routes here +if [[ "$SERVER_URL" == "http://localhost:3001" || "$SERVER_URL" == "http://localhost:3002" || "$SERVER_URL" == "https://sandbox.shuffler.io" || "$SERVER_URL" == "https://shuffler.io" ]]; then +Routes+=( +'homepage' +'home' +'contact' +'workflows/fa9314a7-bb9b-4b41-8885-1d02b199f04d' +'services' +'debug' +'creator' +'articles' +'partners' +'pricing' +'pricing2' +'training' +'professional-services' +'detections' +) +fi + +# Add all common routes here +Routes+=( +'workflows' +'workflows?tab=org_workflows' +'workflows?tab=my_workflows' +'workflows?tab=all_workflows' +'usecases' +'usecases2' +'getting-started' +'welcome' +'health' +'docs' +'settings' +'apps/new' +'apps/gmail' +'apis/gmail' +'forms' +'apps' +'apps?tab=my_apps' +'apps?tab=all_apps' +'search' +'search?tab=org_apps' +'search?tab=my_apps' +'search?tab=workflows' +'search?tab=docs' +'search?tab=creators' +'search?tab=discord' +"admin?tab=organization" +"admin?tab=users" +"admin?tab=app_auth" +"admin?tab=datastore" +"admin?tab=files" +"admin?tab=triggers" +"admin?tab=locations" +"admin?tab=tenants" +"admin?admin_tab=org_config" +"admin?admin_tab=sso" +"admin?admin_tab=notifications" +"admin?admin_tab=billingstats" +"admin?admin_tab=branding(beta)" +) + +ALL_ROUTES=("${Routes[@]}") + +# Stop frontend container to so it test unpushed changes to shuffle-frontend container +docker stop shuffle-frontend + +# Install dependencies +yarn install +echo "Starting frontend..." + +BROWSER=none yarn start & +SERVER_PID=$! +echo "Frontend started with PID: $SERVER_PID" + +echo "Waiting for 1 minute to ensure the server is fully up..." +sleep 60 + +echo "Server is up! Starting Selenium tests..." + +echo "Starting frontend tests..." +node selenium-test.js "$SERVER_URL" "${ALL_ROUTES[@]}" + +TEST_EXIT_CODE=$? + +if [[ $TEST_EXIT_CODE -ne 0 ]]; then +echo "Selenium tests failed. Exiting..." +kill $SERVER_PID +exit 1 +fi + +kill $SERVER_PID +echo "Testing complete. See above logs for errors if any." + +# Starting frontend container +echo "Starting shuffle-frontend container..." +docker start shuffle-frontend +echo "shuffle-frontend started successfully." +exit 0 \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 7426ee23..4f6d2166 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -88,6 +88,7 @@ "remark-rehype": "^11.0.0", "rsuite": "^5.23.0", "search-insights": "^2.2.1", + "selenium-webdriver": "^4.28.1", "shellwords": "^1.0.1", "simplebar": "^4.2.3", "styled-components": "^4.4.1", @@ -98,7 +99,7 @@ "zone.js": "~0.8.26" }, "scripts": { - "start": "HTTPS=false&&PORT=3000 GENERATE_SOURCEMAP=false react-scripts --openssl-legacy-provider start", + "start": "set HTTPS=false&& set PORT=3000&& set GENERATE_SOURCEMAP=true&& react-scripts --openssl-legacy-provider start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", diff --git a/frontend/selenium-test.js b/frontend/selenium-test.js new file mode 100644 index 00000000..eb9b731e --- /dev/null +++ b/frontend/selenium-test.js @@ -0,0 +1,249 @@ +const { Builder, By, until } = require('selenium-webdriver'); +const chrome = require('selenium-webdriver/chrome'); +const fs = require('fs'); +const path = require('path'); + +console.log('Starting Selenium script for testing pages...'); + +(async () => { + const userDataDir = path.join(__dirname, 'chrome-user-data', `${Date.now()}-${Math.random().toString(36).substring(2, 8)}`); + fs.mkdirSync(userDataDir, { recursive: true }); + + let options = new chrome.Options(); + options.addArguments('--no-sandbox'); + options.addArguments('--headless'); + + let driver = await new Builder() + .forBrowser('chrome') + .setChromeOptions(options) + .build(); + + const SuccessfullyLoadedPath = []; + const FailedToLoadPath = []; + + try { + await driver.manage().window().setRect({ width: 1600, height: 1200 }); + await driver.manage().setTimeouts({ implicit: 15000 }); + + const frontendURL = process.argv[2]; + const routes = process.argv.slice(3); + + console.log('Frontend URL:', frontendURL); + const isCloud = frontendURL === 'http://localhost:3002' || frontendURL === 'https://sandbox.shuffler.io' || frontendURL === 'https://shuffler.io'; + + if (isCloud) { + // Login Credentials + const LOGIN_URL = `${frontendURL}/login`; + const USERNAME = 'shuffle-testing@gmail.com'; + const PASSWORD = 'testing@123'; + + console.log('Logging in...'); + await driver.get(LOGIN_URL); + + try { + await driver.wait(until.elementLocated(By.css('#emailfield')), 10000); + await driver.findElement(By.css('#emailfield')).sendKeys(USERNAME); + + await driver.wait(until.elementLocated(By.css('#outlined-password-input')), 10000); + await driver.findElement(By.css('#outlined-password-input')).sendKeys(PASSWORD); + + await driver.wait(until.elementLocated(By.css('#loginButton')), 10000); + await driver.findElement(By.css('#loginButton')).click(); + + // Ensure login success by checking URL change + await driver.wait(async () => { + const url = await driver.getCurrentUrl(); + return url.includes('welcome'); + }, 20000); + + console.log('Successfully logged in!'); + } catch (error) { + console.error('Login failed:', error.message); + FailedToLoadPath.push(LOGIN_URL); + await driver.quit(); + process.exit(1); + } + }else { + // Steps for onprem testing + // 1. Login + // 2. Create new workflow + + // Write your own login credentials here if you are testing onprem locally + const USERNAME = 'demo@demo.io'; + const PASSWORD = 'supercoolpassword'; + const WORKFLOW_URL = `${frontendURL}/workflows`; + + try { + // Login + console.log('Logging in...'); + await driver.get(`${frontendURL}/login`); + + await driver.wait(until.elementLocated(By.css('#emailfield')), 10000); + await driver.findElement(By.css('#emailfield')).sendKeys(USERNAME); + + await driver.wait(until.elementLocated(By.css('#outlined-password-input')), 10000); + await driver.findElement(By.css('#outlined-password-input')).sendKeys(PASSWORD); + + await driver.wait(until.elementLocated(By.css('#loginButton')), 10000); + await driver.findElement(By.css('#loginButton')).click(); + + + // Ensure signup success by checking URL change + await driver.wait(async () => { + const url = await driver.getCurrentUrl(); + return url.includes('welcome') || url.includes('workflows'); + }, 20000); + + + const isParentPresent = await driver.wait(async () => { + return await driver.executeScript( + "return document.querySelector('.parent-component') !== null;" + ); + }, 5000).catch(() => false); + + if (!isParentPresent) { + const logs = await driver.manage().logs().get('browser'); + const severeErrors = logs.filter(log => log.level.name === 'SEVERE'); + if (severeErrors.length > 0) { + const crashCausingErrors = severeErrors.filter(err => { + return !err.message.includes('Warning:') && + !err.message.includes('MUI:') + }); + + console.error(`Page (${frontendURL}/login) did not load correctly:`, crashCausingErrors); + FailedToLoadPath.push(`${frontendURL}/login`); + } + } + + console.log('Successfully logged in!'); + + } catch (error) { + console.error('Failed to login:', error.message); + FailedToLoadPath.push(WORKFLOW_URL); + await driver.quit(); + process.exit(1); + } + + try { + // Create new workflow + console.log('Creating new workflow...'); + await driver.get(WORKFLOW_URL); + + await driver.sleep(1000); + + await driver.wait(until.elementLocated(By.css('#create_workflow_button')), 10000); + const create_workflow_button = await driver.findElement(By.css('#create_workflow_button')); + + await driver.sleep(1000); + await create_workflow_button.click(); + + await driver.sleep(1500); + + await driver.wait(until.elementLocated(By.css('#Enter-Workflow-Name')), 10000); + const enter_workflow_name_field = await driver.findElement(By.css('#Enter-Workflow-Name')); + + await driver.sleep(1000); + await enter_workflow_name_field.sendKeys("Test Workflow"); + + // Wait before saving + await driver.sleep(1500); + + await driver.wait(until.elementLocated(By.css('#save_workflow_button')), 10000); + const save_workflow_button = await driver.findElement(By.css('#save_workflow_button')); + + await driver.sleep(1000); // Delay before clicking + await save_workflow_button.click(); + + // Wait for the parent component to appear + await driver.sleep(2000); + + const isParentPresent = await driver.wait(async () => { + return await driver.executeScript( + "return document.querySelector('.parent-component') !== null;" + ); + }, 5000).catch(() => false); + + if (!isParentPresent) { + const logs = await driver.manage().logs().get('browser'); + const severeErrors = logs.filter(log => log.level.name === 'SEVERE'); + if (severeErrors.length > 0) { + const crashCausingErrors = severeErrors.filter(err => { + return !err.message.includes('Warning:') && + !err.message.includes('MUI:'); + }); + + console.error(`Page (${WORKFLOW_URL}) did not load correctly:`, crashCausingErrors); + FailedToLoadPath.push(`${WORKFLOW_URL}`); + } + } + + } catch (error) { + console.error('Failed to create new workflow:', error.message); + FailedToLoadPath.push(WORKFLOW_URL); + await driver.quit(); + process.exit(1); + } + } + + // Get routes from command line arguments + if (routes.length === 0) { + console.error('No routes found to test.'); + await driver.quit(); + process.exit(1); + } + console.log(`Found ${routes.length} routes to test.`); + + for (const route of routes) { + const url = `${frontendURL}/${route}`; + console.log(`Testing route: ${url}`); + + try { + await driver.get(url); + await driver.sleep(3000); + + // Wait for document readiness + await driver.wait(async () => { + return await driver.executeScript('return document.readyState') === 'complete'; + }, 10000); + + const isParentPresent = await driver.wait(async () => { + return await driver.executeScript( + "return document.querySelector('.parent-component') !== null;" + ); + }, 5000).catch(() => false); + + if (!isParentPresent) { + const logs = await driver.manage().logs().get('browser'); + const severeErrors = logs.filter(log => log.level.name === 'SEVERE'); + if (severeErrors.length > 0) { + const crashCausingErrors = severeErrors.filter(err => { + return !err.message.includes('Warning:') && + !err.message.includes('MUI:') + }); + + console.error(`Page (${url}) did not load correctly:`, crashCausingErrors); + FailedToLoadPath.push(url); + } + continue; + } + + console.log(`Successfully loaded: ${url}`); + SuccessfullyLoadedPath.push(url); + } catch (error) { + console.error(`Failed to load ${url}:`, error.message); + } + } + } finally { + console.log("Total pages tested: ", SuccessfullyLoadedPath.length + FailedToLoadPath.length); + console.log("Successfully loaded pages: ", SuccessfullyLoadedPath.length); + if (FailedToLoadPath.length > 0) { + console.log("Failed to load pages: ", FailedToLoadPath.length); + console.log("Failed to load pages paths: ", FailedToLoadPath); + process.exit(1); + }else { + console.log("No pages failed to load. Congrats!"); + } + await driver.quit(); + fs.rmSync(userDataDir, { recursive: true, force: true }); + } +})(); \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 8c0e8870..03d4c77e 100755 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -186,6 +186,7 @@ const App = (message, props) => { color: "rgba(255, 255, 255, 0.65)", minHeight: "100vh", }} + className='parent-component' > { + {userdata.has_card_available === true ? - - ))} + style={{ + width: "100%", + justifyContent: "flex-start", + padding: "12px", + marginBottom: "12px", + backgroundColor: "#1A1A1A", + border: "1px solid #494949", + borderRadius: "8px", + color: "white", + opacity: 0.7, + cursor: option.valid === true ? "pointer" : "not-allowed", + }} + disabled={option.valid === false} + > + {option.name} + + {option.name} + + + + ) + })}
{ */} @@ -282,7 +294,7 @@ const LoginPage = props => { const [register, setRegister] = useState(inregister); const [checkboxClicked, setCheckboxClicked] = useState(false); const [loginWithSSO, setLoginWithSSO] = useState(false) - + const [showPassword, setShowPassword] = useState(false) const [ssoUrl, setSSOUrl] = useState(""); const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" || window.location.host === "migration.shuffler.io"; @@ -315,7 +327,15 @@ const LoginPage = props => { } else { document.title = "Register to Shuffle SaaS" } - } + } + + useEffect(() => { + + if (loginWithSSO && window?.location?.pathname === "/register") { + setLoginWithSSO(false) + } + + },[window?.location?.pathname]) // Just a way to force location loading properly // Register & login should be split :3 @@ -354,7 +374,8 @@ const LoginPage = props => { color: "white", padding: "40px", flex: 1, - maxWidth: isMobile ? "100%" : "550px", + maxWidth: isMobile ? "100%" : 410, + minWidth: 410, background: "#212121", borderRadius: "12px", display: "flex", @@ -649,26 +670,16 @@ const LoginPage = props => { setLoginWithSSO(true) } - //const onClickRegister = () => { - // if (props.location.pathname === "/login") { - // window.location.pathname = "/register" - // } else { - // window.location.pathname = "/login" - // } - - // setLoginCheck(!register) - //} - - //var loginChange = register ? (

Want to register? Click here.

) : (

Go back to login? Click here.

); var formtitle = register ?
Welcome Back!
:
Create your account
var formButton = !isCloud ? "" : register ?
Don’t have an account yet?
Register here
: <> +
Already have an account?
Login here
//
Click here to Login
// {formtitle} - const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)" + const buttonBackground = "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)" const buttonStyle = { borderRadius: 25, height: 50, fontSize: 18, backgroundImage: handleValidateForm(username, password) || loginLoading || (checkboxClicked && register) ? buttonBackground : "grey", color: "white" } //
- {ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0 ? ( + {( + ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length) > 0 + //|| (isCloud && !loginWithSSO && window?.location?.pathname !== "/register") + ? (
Or
@@ -916,11 +944,17 @@ const LoginPage = props => { color="secondary" variant="outlined" type="button" - style={{ flex: "1", marginTop: 5 }} - onClick={() => { + style={{ flex: "1", marginTop: 5, textTransform: 'none', fontSize: 16 }} + onClick={(e) => { //console.log("CLICK SSO"); - window.location.href = ssoUrl + e.preventDefault(); //navigate(ssoUrl) + if (isCloud) { + setLoginWithSSO(true) + setPassword("") + }else { + window.location.href = ssoUrl + } }} > Use SSO @@ -928,6 +962,19 @@ const LoginPage = props => {
) : null} + {isCloud && loginWithSSO && ( + + )} @@ -951,7 +998,7 @@ const LoginPage = props => { return ( -
+
{loadedCheck}
) From 95f6e3c698a378a854797b4c40b33e72ce6dc745 Mon Sep 17 00:00:00 2001 From: Frikky Date: Thu, 20 Mar 2025 17:22:54 +0100 Subject: [PATCH 30/32] Remapped back to using SHUFFLE_BASE_IMAGE_NAME with default frikky/shuffle: https://github.com/Shuffle/Shuffle/issues/1660 --- functions/onprem/worker/worker.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 943dcd2f..16e2b76e 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -57,11 +57,10 @@ var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP")) var swarmNetworkName = os.Getenv("SHUFFLE_SWARM_NETWORK_NAME") var dockerApiVersion = strings.ToLower(os.Getenv("DOCKER_API_VERSION")) -var baseimagename = "frikky/shuffle" var kubernetesNamespace = os.Getenv("KUBERNETES_NAMESPACE") var executionCount int64 -// var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME") +var baseimagename = os.Getenv("SHUFFLE_BASE_IMAGE_NAME") // var baseimagename = "registry.hub.docker.com/frikky/shuffle" var registryName = "registry.hub.docker.com" @@ -3724,7 +3723,7 @@ func main() { } if baseimagename == "" { - log.Printf("[DEBUG] Setting baseimagename") + log.Printf("[DEBUG] Setting baseimagename to frikky/shuffle") baseimagename = "frikky/shuffle" // Dockerhub //baseimagename = "shuffle" // Github (ghcr.io) } From 373e5bc9092161acf1330cdf3d89dd9c841d42a8 Mon Sep 17 00:00:00 2001 From: Frikky Date: Fri, 21 Mar 2025 02:03:19 +0100 Subject: [PATCH 31/32] Multiple minor fixes for 2.0.1 release to improve general usability --- frontend/src/components/LeftSideBar.jsx | 10 +- frontend/src/components/LicencePopup.jsx | 11 +- frontend/src/components/Navbar.jsx | 2 +- .../src/components/OrgHeaderexpandedNew.jsx | 48 +++++--- frontend/src/components/ParsedAction.jsx | 2 +- .../src/components/ShuffleCodeEditor1.jsx | 3 +- .../components/WorkflowValidationTimeline.jsx | 10 +- frontend/src/theme.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 107 ++++++++++++------ frontend/src/views/Docs.jsx | 7 +- frontend/src/views/RunWorkflow.jsx | 2 + frontend/src/views/Workflows2.jsx | 17 ++- 12 files changed, 145 insertions(+), 76 deletions(-) diff --git a/frontend/src/components/LeftSideBar.jsx b/frontend/src/components/LeftSideBar.jsx index 6ef36775..baf60f06 100644 --- a/frontend/src/components/LeftSideBar.jsx +++ b/frontend/src/components/LeftSideBar.jsx @@ -526,7 +526,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { - Version: 2.0.0 + Version: 2.0.1 @@ -773,6 +773,9 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => { }, [window?.location?.pathname]); + const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); + + return (
{ transition: "width 0.3s ease", boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.2)" , resize: 'both', - - zoom: 0.8, + zoom: isSafari ? undefined : 0.8, + transform: isSafari ? "scale(0.8)" : undefined, + transformOrigin: isSafari ? "top left" : undefined, height: "calc((100vh - 32px)*1.2)", }} > diff --git a/frontend/src/components/LicencePopup.jsx b/frontend/src/components/LicencePopup.jsx index 9492ae99..67fb07b4 100644 --- a/frontend/src/components/LicencePopup.jsx +++ b/frontend/src/components/LicencePopup.jsx @@ -909,21 +909,23 @@ const LicencePopup = (props) => { const priceItem = window.location.origin === "https://shuffler.io/" || "https://sandbox.shuffler.io/" ? shuffleVariant === 0 - ? "app_executions" - : "cores" + ? "price_1PWI3uDzMUgUjxHSffUBwWCy" + : "price_1PWI8EDzMUgUjxHSfEhUB7oL" + : shuffleVariant === 0 ? "price_1PZPSSEJjT17t98NLJoTMYja" : "price_1PZPQuEJjT17t98N3yORUtd9"; const successUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=success` const failUrl = `${window.location.origin}/admin?admin_tab=billingstats&payment=failure` + const quantity = shuffleVariant === 0 ? selectedValue / 100 : selectedValue - console.log("Priceitem: ", priceItem, shuffleVariant) + console.log("Priceitem: ", priceItem, quantity, shuffleVariant) var checkoutObject = { lineItems: [ { price: priceItem, - quantity: shuffleVariant === 0 ? selectedValue / 100 : selectedValue, + quantity: quantity, }, ], mode: "subscription", @@ -1141,6 +1143,7 @@ const LicencePopup = (props) => { > View all plans +