## Description - Fix cypress local run #### PR fixes following issue(s) Fixes # (issue number) > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
229 lines
7.2 KiB
JavaScript
229 lines
7.2 KiB
JavaScript
const { Client } = require("pg");
|
|
const os = require("os");
|
|
const AWS = require("aws-sdk");
|
|
const fs = require("fs");
|
|
|
|
exports.cypressHooks = cypressHooks;
|
|
|
|
// This function will helps to check and get env variables
|
|
function getEnvValue(varName, { required = true }) {
|
|
if (required && process.env[varName] === undefined) {
|
|
throw Error(
|
|
`Please check some or all the following ENV variables are not set properly [ RUNID, ATTEMPT_NUMBER, REPOSITORY, COMMITTER, TAG, BRANCH, THIS_RUNNER, CYPRESS_DB_USER, CYPRESS_DB_HOST, CYPRESS_DB_NAME, CYPRESS_DB_PWD, CYPRESS_S3_ACCESS, CYPRESS_S3_SECRET ].`,
|
|
);
|
|
}
|
|
const value =
|
|
process.env[varName] === undefined ? "Cypress test" : process.env[varName];
|
|
return value;
|
|
}
|
|
|
|
//This is to setup the db client
|
|
function configureDbClient() {
|
|
const dbConfig = {
|
|
user: getEnvValue("CYPRESS_DB_USER", { required: true }),
|
|
host: getEnvValue("CYPRESS_DB_HOST", { required: true }),
|
|
database: getEnvValue("CYPRESS_DB_NAME", { required: true }),
|
|
password: getEnvValue("CYPRESS_DB_PWD", { required: true }),
|
|
port: 5432,
|
|
ssl: true,
|
|
keepalives: 0,
|
|
};
|
|
|
|
const dbClient = new Client(dbConfig);
|
|
dbClient.connect();
|
|
|
|
return dbClient;
|
|
}
|
|
|
|
// This is to setup the AWS client
|
|
function configureS3() {
|
|
AWS.config.update({ region: "ap-south-1" });
|
|
const s3client = new AWS.S3({
|
|
credentials: {
|
|
accessKeyId: getEnvValue("CYPRESS_S3_ACCESS", { required: true }),
|
|
secretAccessKey: getEnvValue("CYPRESS_S3_SECRET", { required: true }),
|
|
},
|
|
});
|
|
return s3client;
|
|
}
|
|
|
|
// This is to upload files to s3 when required
|
|
function uploadToS3(s3Client, filePath, key) {
|
|
const fileContent = fs.readFileSync(filePath);
|
|
|
|
const params = {
|
|
Bucket: "appsmith-internal-cy-db",
|
|
Key: key,
|
|
Body: fileContent,
|
|
};
|
|
return s3Client.upload(params).promise();
|
|
}
|
|
|
|
async function cypressHooks(on, config) {
|
|
const s3 = configureS3();
|
|
const dbClient = configureDbClient();
|
|
const runData = {
|
|
commitMsg: getEnvValue("COMMIT_INFO_MESSAGE", { required: false }),
|
|
workflowId: getEnvValue("RUNID", { required: true }),
|
|
attempt: getEnvValue("ATTEMPT_NUMBER", { required: true }),
|
|
os: os.type(),
|
|
repo: getEnvValue("REPOSITORY", { required: true }),
|
|
committer: getEnvValue("COMMITTER", { required: true }),
|
|
type: getEnvValue("TAG", { required: true }),
|
|
branch: getEnvValue("BRANCH", { required: true }),
|
|
};
|
|
const matrix = {
|
|
matrixId: getEnvValue("THIS_RUNNER", { required: true }),
|
|
matrixStatus: "started",
|
|
};
|
|
|
|
const specData = {};
|
|
|
|
await on("before:run", async (runDetails) => {
|
|
runData.browser = runDetails.browser.name;
|
|
try {
|
|
const runResponse = await dbClient.query(
|
|
`INSERT INTO public.attempt ("workflowId", "attempt", "browser", "os", "repo", "committer", "type", "commitMsg", "branch")
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
ON CONFLICT ("workflowId", attempt) DO NOTHING
|
|
RETURNING id;`,
|
|
[
|
|
runData.workflowId,
|
|
runData.attempt,
|
|
runData.browser,
|
|
runData.os,
|
|
runData.repo,
|
|
runData.committer,
|
|
runData.type,
|
|
runData.commitMsg,
|
|
runData.branch,
|
|
],
|
|
);
|
|
|
|
if (runResponse.rows.length > 0) {
|
|
runData.attemptId = runResponse.rows[0].id; // Save the inserted attempt ID for later updates
|
|
} else {
|
|
const res = await dbClient.query(
|
|
`SELECT id FROM public.attempt WHERE "workflowId" = $1 AND attempt = $2`,
|
|
[runData.workflowId, runData.attempt],
|
|
);
|
|
runData.attemptId = res.rows[0].id;
|
|
}
|
|
|
|
const matrixResponse = await dbClient.query(
|
|
`INSERT INTO public.matrix ("workflowId", "matrixId", "status", "attemptId")
|
|
VALUES ($1, $2, $3, $4)
|
|
ON CONFLICT ("matrixId", "attemptId") DO NOTHING
|
|
RETURNING id;`,
|
|
[
|
|
runData.workflowId,
|
|
matrix.matrixId,
|
|
matrix.matrixStatus,
|
|
runData.attemptId,
|
|
],
|
|
);
|
|
matrix.id = matrixResponse.rows[0].id; // Save the inserted matrix ID for later updates
|
|
} catch (err) {
|
|
console.log(err);
|
|
}
|
|
});
|
|
|
|
await on("before:spec", async (spec) => {
|
|
specData.name = spec.relative;
|
|
specData.matrixId = matrix.id;
|
|
try {
|
|
const specResponse = await dbClient.query(
|
|
'INSERT INTO public.specs ("name", "matrixId") VALUES ($1, $2) RETURNING id',
|
|
[specData.name, matrix.id],
|
|
);
|
|
specData.specId = specResponse.rows[0].id; // Save the inserted spec ID for later updates
|
|
} catch (err) {
|
|
console.log(err);
|
|
}
|
|
});
|
|
|
|
await on("after:spec", async (spec, results) => {
|
|
specData.testCount = results.stats.tests;
|
|
specData.passes = results.stats.passes;
|
|
specData.failed = results.stats.failures;
|
|
specData.pending = results.stats.pending;
|
|
specData.skipped = results.stats.skipped;
|
|
specData.status = results.stats.failures > 0 ? "fail" : "pass";
|
|
|
|
try {
|
|
await dbClient.query(
|
|
'UPDATE public.specs SET "testCount" = $1, "passes" = $2, "failed" = $3, "skipped" = $4, "pending" = $5, "status" = $6 WHERE id = $7',
|
|
[
|
|
results.stats.tests,
|
|
results.stats.passes,
|
|
results.stats.failures,
|
|
results.stats.skipped,
|
|
results.stats.pending,
|
|
specData.status,
|
|
specData.specId,
|
|
],
|
|
);
|
|
for (const test of results.tests) {
|
|
const testResponse = await dbClient.query(
|
|
`INSERT INTO public.tests ("name", "specId", "status", "retries", "retryData") VALUES ($1, $2, $3, $4, $5) RETURNING id`,
|
|
[
|
|
test.title[1],
|
|
specData.specId,
|
|
test.state,
|
|
test.attempts.length,
|
|
JSON.stringify(test.attempts),
|
|
],
|
|
);
|
|
if (
|
|
test.attempts.some((attempt) => attempt.state === "failed") &&
|
|
results.screenshots
|
|
) {
|
|
const out = results.screenshots.filter(
|
|
(scr) => scr.testId === test.testId,
|
|
);
|
|
console.log("Uploading screenshots...");
|
|
for (const scr of out) {
|
|
const key = `${testResponse.rows[0].id}_${specData.specId}_${
|
|
scr.testAttemptIndex + 1
|
|
}`;
|
|
Promise.all([uploadToS3(s3, scr.path, key)]).catch((error) => {
|
|
console.log("Error in uploading screenshots:", error);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (
|
|
results.tests.some((test) =>
|
|
test.attempts.some((attempt) => attempt.state === "failed"),
|
|
) &&
|
|
results.video
|
|
) {
|
|
console.log("Uploading video...");
|
|
const key = `${specData.specId}`;
|
|
Promise.all([uploadToS3(s3, results.video, key)]).catch((error) => {
|
|
console.log("Error in uploading video:", error);
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.log(err);
|
|
}
|
|
});
|
|
|
|
on("after:run", async (runDetails) => {
|
|
try {
|
|
await dbClient.query(
|
|
`UPDATE public.matrix SET "status" = $1 WHERE id = $2`,
|
|
["done", matrix.id],
|
|
);
|
|
await dbClient.query(
|
|
`UPDATE public.attempt SET "endTime" = $1 WHERE "id" = $2`,
|
|
[new Date(), runData.attemptId],
|
|
);
|
|
await dbClient.end();
|
|
} catch (err) {
|
|
console.log(err);
|
|
}
|
|
});
|
|
}
|