[Feature] - Add frontend testing script for onprem

This commit is contained in:
lalitdeore12@gmail.com
2025-02-20 18:45:05 +05:30
parent 05880a04b3
commit 2d00444a5a
8 changed files with 377 additions and 3 deletions
+17 -2
View File
@@ -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')
+104
View File
@@ -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
+2 -1
View File
@@ -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",
+249
View File
@@ -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 });
}
})();
+1
View File
@@ -186,6 +186,7 @@ const App = (message, props) => {
color: "rgba(255, 255, 255, 0.65)",
minHeight: "100vh",
}}
className='parent-component'
>
<ScrollToTop
getUserNotifications={getUserNotifications}
+2
View File
@@ -298,6 +298,7 @@ const EditWorkflow = (props) => {
<Button
variant="contained"
style={{}}
id="save_workflow_button"
disabled={name.length === 0 || submitLoading === true}
onClick={() => {
setSubmitLoading(true)
@@ -402,6 +403,7 @@ const EditWorkflow = (props) => {
label="Name"
autoFocus
fullWidth
id="Enter-Workflow-Name"
/>
<div style={{ display: "flex", marginTop: 10, }}>
+1
View File
@@ -477,6 +477,7 @@ const LoginDialog = (props) => {
variant="contained"
type="submit"
style={{ flex: "1" }}
id="loginButton"
disabled={!handleValidateForm() || loginLoading}
>
{loginLoading ? (
+1
View File
@@ -4601,6 +4601,7 @@ const Workflows2 = (props) => {
variant="contained"
color="primary"
onClick={handleCreateWorkflow}
id="create_workflow_button"
style={{
borderRadius: 4,
flex: 0.8,