## Description As a user, I want to run my backend server in local machine for running cypress test cases. Fixes #`35635` ## Automation /ok-to-test tags="@tag.Sanity" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/10469851734> > Commit: 33c2dbbcf389a6840830e8914738a068d8ab41d9 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10469851734&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Sanity` > Spec: > <hr>Tue, 20 Aug 2024 10:54:22 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No <img width="1440" alt="Screenshot 2024-08-12 at 10 12 04 PM" src="https://github.com/user-attachments/assets/4ad51bb1-30f6-4d6c-aa60-f658848492b0"> <img width="1440" alt="Screenshot 2024-08-12 at 10 12 10 PM" src="https://github.com/user-attachments/assets/03a3242b-302f-422c-b0df-c9da5f08d0c1"> <img width="1440" alt="Screenshot 2024-08-12 at 10 13 03 PM" src="https://github.com/user-attachments/assets/00b71397-12a4-4471-b6a5-83642d7edc83"> <img width="1440" alt="Screenshot 2024-08-12 at 10 13 10 PM" src="https://github.com/user-attachments/assets/2d15a205-95fa-4201-b1d1-45453064da8d"> <img width="1029" alt="Screenshot 2024-08-12 at 10 14 11 PM" src="https://github.com/user-attachments/assets/e1778a43-9690-42ca-8adc-351d7ec95206"> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new function to manage local server setup using Docker Compose, enhancing the setup process. - Added user prompts to guide decisions regarding local server setup. - **Improvements** - Implemented a retry mechanism to check Docker services, improving reliability during setup. - Enhanced error handling to provide clear feedback when setup conditions are not met. - **Refactor** - Integrated new functionalities into the existing setup process for a smoother user experience. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
281 lines
8.7 KiB
JavaScript
281 lines
8.7 KiB
JavaScript
const { exec, execSync } = require("child_process");
|
|
const { existsSync, readFileSync, writeFileSync } = require("fs");
|
|
const path = require("path");
|
|
const prompt = require("prompt-sync")();
|
|
|
|
function isContainerRunning(containerName) {
|
|
try {
|
|
const output = execSync(
|
|
`docker ps --filter "name=^/${containerName}$" --format '{{.Names}}'`,
|
|
);
|
|
return output.length > 0;
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Helper function to execute commands and return a Promise
|
|
async function execCommand(command, options) {
|
|
return new Promise((resolve, reject) => {
|
|
exec(command, options, (error, stdout, stderr) => {
|
|
if (error) {
|
|
reject(error);
|
|
} else {
|
|
resolve({ stdout, stderr });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function checkDockerCompose() {
|
|
try {
|
|
execSync("docker-compose --version", { stdio: "ignore" });
|
|
return true;
|
|
} catch (error) {
|
|
console.error(
|
|
"ERROR: docker-compose is not installed. Please install Docker Compose.",
|
|
);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
async function runLocalServer() {
|
|
try {
|
|
let user_input = prompt(
|
|
`Do you wish to continue without setting up the local server with docker? (yes/no): `,
|
|
);
|
|
user_input = (user_input || "").trim().toLowerCase();
|
|
|
|
if (user_input === "yes" || user_input === "y") {
|
|
console.log(
|
|
"INFO",
|
|
"Continuing without setting up local backend docker based server.",
|
|
);
|
|
} else {
|
|
// Adjust the path to point to the correct directory
|
|
const dockerDir = path.join(__dirname, "../../../../deploy/docker");
|
|
|
|
if (!existsSync(dockerDir)) {
|
|
console.error(`ERROR: Directory ${dockerDir} does not exist.`);
|
|
process.exit(1); // Exit if the directory is missing
|
|
}
|
|
|
|
await checkDockerCompose(); // Ensure Docker Compose is available
|
|
|
|
console.log("INFO: Starting local server using Docker Compose...");
|
|
execSync(`cd ${dockerDir} && pwd && docker-compose up -d`, {
|
|
stdio: "inherit",
|
|
});
|
|
|
|
// Wait for the services to be fully up and running
|
|
let servicesRunning = false;
|
|
const maxRetries = 30;
|
|
const retryInterval = 7000; // 7 seconds
|
|
|
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
try {
|
|
const { stdout } = await execCommand("docker-compose ps", {
|
|
cwd: dockerDir,
|
|
});
|
|
if (stdout.includes("Up")) {
|
|
servicesRunning = true;
|
|
break;
|
|
}
|
|
} catch (error) {
|
|
console.error("ERROR: Error checking service status:", error.message);
|
|
process.exit(1); // Exit if checking service status fails
|
|
}
|
|
console.log(
|
|
`INFO: Waiting for services to be fully up and running... (attempt ${attempt}/${maxRetries})`,
|
|
);
|
|
await new Promise((resolve) => setTimeout(resolve, retryInterval));
|
|
}
|
|
if (servicesRunning) {
|
|
console.log("INFO: Local server is up and running.");
|
|
return true;
|
|
} else {
|
|
console.error(
|
|
"ERROR: Services did not become available within the expected time.",
|
|
);
|
|
process.exit(1); // Exit if services are not up
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("ERROR: Error starting local server:", error.message);
|
|
process.exit(1); // Exit if starting local server fails
|
|
}
|
|
}
|
|
|
|
function ensureTEDIsRunning() {
|
|
// Check if TED is running. If not, then ask user if they wish to pull and run the TED container
|
|
const isTedRunning = isContainerRunning("ted");
|
|
|
|
if (isTedRunning) {
|
|
console.log("INFO", "TED (TestEventDriver) is already running");
|
|
} else {
|
|
try {
|
|
let user_input = prompt(
|
|
"TED (TestEventDriver) is not running. Do you want to pull & run the latest Docker container for TED (TestEventDriver)? (yes/no): ",
|
|
);
|
|
user_input = user_input.trim().toLowerCase();
|
|
switch (user_input) {
|
|
case "yes":
|
|
case "y":
|
|
console.log(
|
|
"INFO",
|
|
"Running the Docker container for TED (TestEventDriver)",
|
|
);
|
|
try {
|
|
execSync(
|
|
"docker run --name ted --rm -d --pull always -p 2022:22 -p 5001:5001 -p 3306:3306 -p 28017:27017 -p 5433:5432 -p 25:25 -p 4200:4200 appsmith/test-event-driver",
|
|
{ stdio: "inherit" },
|
|
);
|
|
console.log(
|
|
"INFO",
|
|
"Please check https://github.com/appsmithorg/TestEventDriver for more details and functionalities of TED",
|
|
);
|
|
} catch (error) {
|
|
console.error("ERROR", `Error installing TED: ${error.message}`);
|
|
}
|
|
break;
|
|
case "no":
|
|
case "n":
|
|
console.log("INFO", "Proceeding without TED");
|
|
break;
|
|
default:
|
|
console.log("ERROR", "Invalid input. Please enter yes or no.");
|
|
process.exit(1);
|
|
}
|
|
} catch (error) {
|
|
console.error("ERROR", `Error: ${error.message}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function checkIfAppsmithIsRunning(baseUrl) {
|
|
// Check if appsmith is running. If it's not running, check if we want the user to continue without it.
|
|
let isDevAppsmithAccessible;
|
|
try {
|
|
const response = await fetch(baseUrl);
|
|
isDevAppsmithAccessible = response.ok;
|
|
} catch (error) {
|
|
console.error(
|
|
"ERROR",
|
|
`Error checking availability of dev.appsmith.com: ${error.message}`,
|
|
);
|
|
isDevAppsmithAccessible = false;
|
|
}
|
|
|
|
if (!isDevAppsmithAccessible) {
|
|
let user_input = prompt(
|
|
`https://dev.appsmith.com is not accessible. Do you wish to continue without setting it up? (yes/no): `,
|
|
);
|
|
user_input = user_input.trim().toLowerCase();
|
|
switch (user_input) {
|
|
case "yes":
|
|
case "y":
|
|
console.log("INFO", "Continuing without setting up dev.appsmith.com");
|
|
break;
|
|
case "no":
|
|
case "n":
|
|
process.exit(1);
|
|
default:
|
|
console.log("ERROR", "Invalid input. Please enter yes or no.");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getBaseUrl(repoRoot) {
|
|
try {
|
|
const cypressConfig = readFileSync(`${repoRoot}/cypress.config.ts`, "utf8");
|
|
const baseUrlMatch = cypressConfig.match(/baseUrl\s*:\s*"([^"]+)"/);
|
|
if (baseUrlMatch) {
|
|
baseUrl = baseUrlMatch[1];
|
|
console.log(
|
|
"INFO",
|
|
`Base url is ${baseUrl}. Please verify if it is correct. If not, please update it in cypress.config.ts file.`,
|
|
);
|
|
return baseUrl;
|
|
} else {
|
|
console.error(
|
|
"ERROR",
|
|
"Base url not found in cypress.config.ts. Please configure `baseUrl` property in cypress.config.ts file.",
|
|
);
|
|
process.exit(1);
|
|
}
|
|
} catch (err) {
|
|
if (err.code === "ENOENT") {
|
|
console.error("ERROR", "cypress.config.ts file not found");
|
|
} else {
|
|
console.error("ERROR", "Error reading cypress.config.ts file:", err);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
function ensureCypressEnvFileExists(repoRoot) {
|
|
// Check if cypress.env.json file exists. If not, create it.
|
|
const filePath = `${repoRoot}/cypress.env.json`;
|
|
if (!existsSync(filePath)) {
|
|
const testEnvData = {
|
|
USERNAME: "testUser@test.com",
|
|
PASSWORD: "testPass",
|
|
TESTUSERNAME1: "viewerappsmith@test.com",
|
|
TESTPASSWORD1: "viewerPass",
|
|
TESTUSERNAME2: "developerappsmith@test.com",
|
|
TESTPASSWORD2: "developerPass",
|
|
};
|
|
writeFileSync(filePath, JSON.stringify(testEnvData, null, 2));
|
|
console.log("INFO", `${repoRoot}/cypress.env.json file created`);
|
|
} else {
|
|
console.log("INFO", `${repoRoot}/cypress.env.json file already exists`);
|
|
}
|
|
}
|
|
|
|
async function setupCypress() {
|
|
// Get the baseUrl from cypress.config.ts file
|
|
let repoRoot = path.join(__dirname, "..", "..");
|
|
let baseUrl = getBaseUrl(repoRoot);
|
|
await runLocalServer();
|
|
await checkIfAppsmithIsRunning(baseUrl);
|
|
|
|
// Install Cypress using yarn install on the app/client repository
|
|
console.log("INFO", "Installing Cypress..");
|
|
try {
|
|
execSync("yarn install", { cwd: `${repoRoot}` });
|
|
} catch (error) {
|
|
console.error("ERROR", `Error installing Cypress: ${error.message}`);
|
|
}
|
|
|
|
ensureCypressEnvFileExists(repoRoot);
|
|
|
|
console.log(
|
|
"INFO",
|
|
"Please add APPSMITH_GIT_ROOT=./container-volumes/git-storage into server-side .env for running Git cases locally along with the server.",
|
|
);
|
|
|
|
ensureTEDIsRunning();
|
|
|
|
console.log(
|
|
"INFO",
|
|
"Please start cypress using the command: npx cypress open",
|
|
);
|
|
console.log(
|
|
"INFO",
|
|
`In order to run single spec, please use the command: cd ${repoRoot} && npx cypress run --spec <specpath> --browser chrome`,
|
|
);
|
|
console.log(
|
|
"INFO",
|
|
"For more details check https://github.com/appsmithorg/appsmith/blob/master/contributions/ClientSetup.md#integration-tests",
|
|
);
|
|
}
|
|
|
|
async function main() {
|
|
await setupCypress();
|
|
process.exit(0);
|
|
}
|
|
|
|
main();
|