[Feature] - Add frontend testing script for onprem
This commit is contained in:
@@ -5,6 +5,7 @@ on:
|
|||||||
workflows: ["dockerbuild"]
|
workflows: ["dockerbuild"]
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -18,8 +19,16 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
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
|
- 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
|
- name: Build the stack
|
||||||
run: docker-compose up -d
|
run: docker-compose up -d
|
||||||
@@ -95,6 +104,12 @@ jobs:
|
|||||||
echo "User registration failed after $MAX_RETRIES attempts."
|
echo "User registration failed after $MAX_RETRIES attempts."
|
||||||
exit 1
|
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
|
- name: Get the API key and run a health check
|
||||||
run: |
|
run: |
|
||||||
RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search')
|
RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search')
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -88,6 +88,7 @@
|
|||||||
"remark-rehype": "^11.0.0",
|
"remark-rehype": "^11.0.0",
|
||||||
"rsuite": "^5.23.0",
|
"rsuite": "^5.23.0",
|
||||||
"search-insights": "^2.2.1",
|
"search-insights": "^2.2.1",
|
||||||
|
"selenium-webdriver": "^4.28.1",
|
||||||
"shellwords": "^1.0.1",
|
"shellwords": "^1.0.1",
|
||||||
"simplebar": "^4.2.3",
|
"simplebar": "^4.2.3",
|
||||||
"styled-components": "^4.4.1",
|
"styled-components": "^4.4.1",
|
||||||
@@ -98,7 +99,7 @@
|
|||||||
"zone.js": "~0.8.26"
|
"zone.js": "~0.8.26"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"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",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -186,6 +186,7 @@ const App = (message, props) => {
|
|||||||
color: "rgba(255, 255, 255, 0.65)",
|
color: "rgba(255, 255, 255, 0.65)",
|
||||||
minHeight: "100vh",
|
minHeight: "100vh",
|
||||||
}}
|
}}
|
||||||
|
className='parent-component'
|
||||||
>
|
>
|
||||||
<ScrollToTop
|
<ScrollToTop
|
||||||
getUserNotifications={getUserNotifications}
|
getUserNotifications={getUserNotifications}
|
||||||
|
|||||||
@@ -298,6 +298,7 @@ const EditWorkflow = (props) => {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
style={{}}
|
style={{}}
|
||||||
|
id="save_workflow_button"
|
||||||
disabled={name.length === 0 || submitLoading === true}
|
disabled={name.length === 0 || submitLoading === true}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSubmitLoading(true)
|
setSubmitLoading(true)
|
||||||
@@ -402,6 +403,7 @@ const EditWorkflow = (props) => {
|
|||||||
label="Name"
|
label="Name"
|
||||||
autoFocus
|
autoFocus
|
||||||
fullWidth
|
fullWidth
|
||||||
|
id="Enter-Workflow-Name"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div style={{ display: "flex", marginTop: 10, }}>
|
<div style={{ display: "flex", marginTop: 10, }}>
|
||||||
|
|||||||
@@ -477,6 +477,7 @@ const LoginDialog = (props) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
type="submit"
|
type="submit"
|
||||||
style={{ flex: "1" }}
|
style={{ flex: "1" }}
|
||||||
|
id="loginButton"
|
||||||
disabled={!handleValidateForm() || loginLoading}
|
disabled={!handleValidateForm() || loginLoading}
|
||||||
>
|
>
|
||||||
{loginLoading ? (
|
{loginLoading ? (
|
||||||
|
|||||||
@@ -4601,6 +4601,7 @@ const Workflows2 = (props) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={handleCreateWorkflow}
|
onClick={handleCreateWorkflow}
|
||||||
|
id="create_workflow_button"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
flex: 0.8,
|
flex: 0.8,
|
||||||
|
|||||||
Reference in New Issue
Block a user