Merge branch 'nightly' of https://github.com/shuffle/shuffle into nightly

This commit is contained in:
Frikky
2025-03-18 13:29:46 +01:00
8 changed files with 387 additions and 24 deletions
+74
View File
@@ -0,0 +1,74 @@
SERVER_URL="http://localhost:3000"
declare -a Routes=($(grep -oP '(?<=path=")[^"]*' src/App.jsx | grep -E '^[a-zA-Z0-9/_:-]+$' | grep -vE '^/$'))
for i in "${!Routes[@]}"; do
Routes[$i]="${Routes[$i]#/}"
done
# # Add all tab routes and unique routes here
Routes+=(
'workflows?tab=org_workflows'
'workflows?tab=my_workflows'
'workflows?tab=all_workflows'
'apps/gmail'
'apis/gmail'
'apps?tab=my_apps'
'apps?tab=all_apps'
'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)"
)
# Stop frontend container so it test unpushed changes
docker stop shuffle-frontend
# # Install all frontend dependencies
yarn add selenium-webdriver --dev && 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" "${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 shuffle-frontend container
echo "Starting shuffle-frontend container..."
docker start shuffle-frontend
echo "shuffle-frontend started successfully."
exit 0
+1
View File
@@ -128,6 +128,7 @@
"babel-preset-es2015": "^6.24.1",
"postcss": "^8.4.38",
"promise-window": "^1.2.1",
"selenium-webdriver": "^4.29.0",
"webpack-cli": "^5.1.4"
}
}
+253
View File
@@ -0,0 +1,253 @@
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().window().maximize()
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`;
// put your login credentials here
const USERNAME = '';
const PASSWORD = '';
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);
const loginButton = await driver.findElement(By.css('#loginButton'));
await driver.executeScript("arguments[0].scrollIntoView(true);", loginButton);
await 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 });
}
})();
+2
View File
@@ -189,6 +189,7 @@ const App = (message, props) => {
color: "rgba(255, 255, 255, 0.65)",
minHeight: "100vh",
}}
className='parent-component'
>
<ScrollToTop
getUserNotifications={getUserNotifications}
@@ -371,6 +372,7 @@ const App = (message, props) => {
element={
<Admin
isLoggedIn={isLoggedIn}
userdata={userdata}
setIsLoggedIn={setIsLoggedIn}
register={true}
isLoaded={isLoaded}
+2
View File
@@ -302,6 +302,7 @@ const EditWorkflow = (props) => {
<Button
variant="contained"
style={{}}
id="save_workflow_button"
disabled={name.length === 0 || submitLoading === true}
onClick={() => {
setSubmitLoading(true)
@@ -406,6 +407,7 @@ const EditWorkflow = (props) => {
label="Name"
autoFocus
fullWidth
id="Enter-Workflow-Name"
/>
<div style={{ display: "flex", marginTop: 10, }}>
-1
View File
@@ -461,7 +461,6 @@ const LoginPage = props => {
})
}
const onSubmit = (e) => {
//toast("Testing from login page")
+1
View File
@@ -4616,6 +4616,7 @@ const Workflows2 = (props) => {
variant="contained"
color="primary"
onClick={handleCreateWorkflow}
id="create_workflow_button"
style={{
borderRadius: 4,
flex: 0.8,