mirror of
https://github.com/ToolJet/ToolJet
synced 2026-04-25 23:47:17 +00:00
599 lines
18 KiB
JavaScript
599 lines
18 KiB
JavaScript
import { commonSelectors, cyParamName } from "Selectors/common";
|
|
import { ssoSelector } from "Selectors/manageSSO";
|
|
import { usersSelector } from "Selectors/manageUsers";
|
|
import { onboardingSelectors } from "Selectors/onboarding";
|
|
import * as common from "Support/utils/common";
|
|
import { fillInputField } from "Support/utils/common";
|
|
import { commonText } from "Texts/common";
|
|
import { ssoText } from "Texts/manageSSO";
|
|
import { usersText } from "Texts/manageUsers";
|
|
|
|
const envVar = Cypress.env("environment");
|
|
|
|
export const verifyManageUsersPageElements = () => {
|
|
cy.get(
|
|
`[data-cy="breadcrumb-header-${cyParamName(commonText.breadcrumbworkspaceSettingTitle)}"]>>`
|
|
).should(($el) => {
|
|
expect($el.contents().first().text().trim()).to.eq(
|
|
commonText.breadcrumbworkspaceSettingTitle
|
|
);
|
|
});
|
|
cy.get(commonSelectors.breadcrumbPageTitle).verifyVisibleElement(
|
|
"have.text",
|
|
"Users"
|
|
);
|
|
|
|
for (const element in usersSelector.usersElements) {
|
|
cy.get(usersSelector.usersElements[element]).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.usersElements[element]
|
|
);
|
|
}
|
|
cy.get(usersSelector.usersPageTitle).should(($el) => {
|
|
expect($el.contents().last().text().trim()).to.eq(usersText.usersPageTitle);
|
|
});
|
|
cy.get(commonSelectors.inputUserSearch).should("be.visible");
|
|
common.searchUser(usersText.adminUserEmail);
|
|
cy.contains("td", usersText.adminUserEmail)
|
|
.parent()
|
|
.within(() => {
|
|
cy.get(
|
|
usersSelector.userName(usersText.adminUserName)
|
|
).verifyVisibleElement("have.text", usersText.adminUserName);
|
|
cy.get(
|
|
usersSelector.userEmail(usersText.adminUserName)
|
|
).verifyVisibleElement("have.text", usersText.adminUserEmail);
|
|
cy.get(
|
|
usersSelector.userStatus(usersText.adminUserName)
|
|
).verifyVisibleElement("have.text", usersText.activeStatus);
|
|
cy.wait(1000);
|
|
cy.get('[data-cy="user-actions-button"]').click();
|
|
});
|
|
|
|
cy.get('[data-cy="edit-user-details-button"]').verifyVisibleElement(
|
|
"have.text",
|
|
"Edit user details"
|
|
);
|
|
cy.get('[data-cy="archive-button"]').verifyVisibleElement(
|
|
"have.text",
|
|
"Archive user"
|
|
);
|
|
|
|
cy.get(usersSelector.userFilterInput).should("be.visible");
|
|
|
|
cy.get(usersSelector.buttonAddUsers)
|
|
.verifyVisibleElement("have.text", usersText.buttonAddUsers)
|
|
.click();
|
|
|
|
cy.get(usersSelector.buttonInviteWithEmail).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.buttonInviteWithEmail
|
|
);
|
|
cy.get(usersSelector.buttonUploadCsvFile).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.buttonUploadCsvFile
|
|
);
|
|
|
|
cy.get(usersSelector.addUsersCardTitle).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.addUsersCardTitle
|
|
);
|
|
|
|
cy.get(commonSelectors.labelFullNameInput).verifyVisibleElement(
|
|
"have.text",
|
|
"Name"
|
|
);
|
|
cy.get(commonSelectors.inputFieldFullName).should("be.visible");
|
|
cy.get(commonSelectors.labelEmailInput).verifyVisibleElement(
|
|
"have.text",
|
|
commonText.labelEmailInput
|
|
);
|
|
|
|
cy.get(commonSelectors.inputFieldEmailAddress).should("be.visible");
|
|
|
|
cy.get(commonSelectors.groupInputFieldLabel).verifyVisibleElement(
|
|
"have.text",
|
|
commonText.groupInputFieldLabel
|
|
);
|
|
cy.wait(1000);
|
|
cy.get('[data-cy="user-group-select"]').should("be.visible");
|
|
cy.get(commonSelectors.cancelButton).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.cancelButton
|
|
);
|
|
cy.get(usersSelector.buttonInviteUsers).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.buttonInviteUsers
|
|
);
|
|
cy.get(commonSelectors.cancelButton).click();
|
|
cy.get(usersSelector.addUsersCardTitle).should("not.exist");
|
|
|
|
cy.get(usersSelector.buttonAddUsers).click();
|
|
cy.get(commonSelectors.closeButton).click();
|
|
cy.get(usersSelector.addUsersCardTitle).should("not.exist");
|
|
|
|
cy.get(usersSelector.buttonAddUsers).click();
|
|
cy.get(usersSelector.addUsersCardTitle).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.addUsersCardTitle
|
|
);
|
|
cy.get(usersSelector.buttonUploadCsvFile).click();
|
|
|
|
if (envVar === "Enterprise") {
|
|
cy.get(usersSelector.helperTextBulkUpload).verifyVisibleElement(
|
|
"have.text",
|
|
"Download the template to add user details or format your file in the same way as the template. Files in any other format may not be recognized. "
|
|
);
|
|
} else {
|
|
cy.get(usersSelector.helperTextBulkUpload).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.helperTextBulkUpload
|
|
);
|
|
}
|
|
cy.get(usersSelector.buttonDownloadTemplate).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.buttonDownloadTemplate
|
|
);
|
|
cy.exec("mkdir -p ./cypress/downloads/", { failOnNonZeroExit: false });
|
|
cy.wait(3000);
|
|
cy.exec("cd ./cypress/downloads/ && rm -rf *", { failOnNonZeroExit: false });
|
|
cy.wait(3000);
|
|
cy.get(usersSelector.buttonDownloadTemplate).click();
|
|
cy.wait(4000);
|
|
cy.exec("ls ./cypress/downloads/").then((result) => {
|
|
const downloadedAppExportFileName = result.stdout.split("\n")[0];
|
|
expect(downloadedAppExportFileName).to.contain.string("sample_upload.csv");
|
|
});
|
|
|
|
cy.get(usersSelector.iconBulkUpload).should("be.visible");
|
|
cy.get(usersSelector.helperTextSelectFile).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.helperTextSelectFile
|
|
);
|
|
cy.get(usersSelector.helperTextDropFile).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.helperTextDropFile
|
|
);
|
|
cy.get(usersSelector.inputFieldBulkUpload).should("exist");
|
|
cy.get(usersSelector.buttonUploadUsers).verifyVisibleElement(
|
|
"have.text",
|
|
usersText.buttonUploadUsers
|
|
);
|
|
};
|
|
|
|
export const inviteUserToWorkspace = (firstName, email) => {
|
|
cy.apiUserInvite(firstName, email);
|
|
fetchAndVisitInviteLink(email);
|
|
cy.clearAndType(onboardingSelectors.loginPasswordInput, "password");
|
|
cy.get(commonSelectors.signUpButton).click();
|
|
cy.get(commonSelectors.acceptInviteButton).click();
|
|
};
|
|
|
|
export const confirmInviteElements = (
|
|
email,
|
|
workspaceName = "My workspace"
|
|
) => {
|
|
cy.get(commonSelectors.signUpSectionHeader).verifyVisibleElement(
|
|
"have.text",
|
|
"Sign up"
|
|
);
|
|
cy.get('[data-cy="signup-info"]').verifyVisibleElement(
|
|
"have.text",
|
|
`Sign up to the workspace - ${workspaceName}. `
|
|
);
|
|
|
|
// cy.verifyLabel("Email")
|
|
// cy.verifyLabel("Create a password")
|
|
cy.get(commonSelectors.invitedUserEmail).verifyVisibleElement(
|
|
"have.text",
|
|
email
|
|
);
|
|
|
|
cy.get(commonSelectors.signUpTermsHelperText).should(($el) => {
|
|
expect($el.contents().first().text().trim()).to.eq(
|
|
commonText.signUpTermsHelperText
|
|
);
|
|
});
|
|
cy.get(commonSelectors.termsOfServiceLink)
|
|
.verifyVisibleElement("have.text", commonText.termsOfServiceLink)
|
|
.and("have.attr", "href")
|
|
.and("equal", "https://www.tooljet.com/terms");
|
|
cy.get(commonSelectors.privacyPolicyLink)
|
|
.verifyVisibleElement("have.text", commonText.privacyPolicyLink)
|
|
.and("have.attr", "href")
|
|
.and("equal", "https://www.tooljet.com/privacy");
|
|
|
|
cy.get("body").then(($el) => {
|
|
if ($el.text().includes("Google")) {
|
|
cy.get(ssoSelector.googleSSOText).verifyVisibleElement(
|
|
"have.text",
|
|
ssoText.googleSignUpText
|
|
);
|
|
cy.get(ssoSelector.gitSSOText).verifyVisibleElement(
|
|
"have.text",
|
|
ssoText.gitSignUpText
|
|
);
|
|
cy.get(commonSelectors.onboardingSeperator).should("be.visible");
|
|
}
|
|
});
|
|
};
|
|
|
|
export const userStatus = (email) => {
|
|
common.navigateToManageUsers();
|
|
common.searchUser(email);
|
|
cy.contains("td", email)
|
|
.parent()
|
|
.within(() => {
|
|
cy.get("td button").click();
|
|
});
|
|
};
|
|
|
|
export const bulkUserUpload = (
|
|
file,
|
|
fileName,
|
|
toastMessage,
|
|
isDuplicate = false
|
|
) => {
|
|
cy.get(usersSelector.inputFieldBulkUpload).selectFile(file, {
|
|
force: true,
|
|
});
|
|
cy.get(usersSelector.uploadedFileData).should("contain", fileName);
|
|
cy.get(usersSelector.buttonUploadUsers).click();
|
|
if (isDuplicate) {
|
|
cy.get(commonSelectors.modalMessage)
|
|
.should("be.visible")
|
|
.and("have.text", toastMessage);
|
|
cy.get(usersSelector.modalClose).click();
|
|
} else {
|
|
cy.get(".go3958317564").should("be.visible").and("have.text", toastMessage);
|
|
cy.get('[data-cy="toast-close-button"]').click();
|
|
}
|
|
cy.wait(1500);
|
|
};
|
|
|
|
export const copyInvitationLink = (firstName, email) => {
|
|
cy.window().then((win) => {
|
|
cy.stub(win, "prompt").returns(win.prompt).as("copyToClipboardPrompt");
|
|
});
|
|
common.searchUser(email);
|
|
cy.contains("td", email)
|
|
.parent()
|
|
.within(() => {
|
|
cy.get('[data-cy="copy-icon"]').click();
|
|
});
|
|
cy.verifyToastMessage(
|
|
commonSelectors.toastMessage,
|
|
usersText.inviteCopiedToast
|
|
);
|
|
cy.get("@copyToClipboardPrompt").then((prompt) => {
|
|
common.logout();
|
|
cy.visit(prompt.args[0][1]);
|
|
});
|
|
};
|
|
|
|
export const fillUserInviteForm = (firstName, email) => {
|
|
cy.get(usersSelector.buttonAddUsers).click();
|
|
fillInputField({ Name: firstName, "Email address": email });
|
|
};
|
|
|
|
export const selectUserGroup = (groupName) => {
|
|
cy.wait(1500);
|
|
cy.get("body").then(($body) => {
|
|
const selectDropdown = $body.find(usersSelector.groupSelector);
|
|
|
|
if (selectDropdown.length === 0) {
|
|
cy.get(usersSelector.groupSelector).click();
|
|
}
|
|
cy.get(usersSelector.groupSelector).eq(0).type(groupName);
|
|
cy.wait(1000);
|
|
cy.get(usersSelector.groupSelectInput).eq(0).check();
|
|
});
|
|
};
|
|
|
|
export const selectGroup = (groupName, timeout = 1000) => {
|
|
cy.get(usersSelector.groupSelector).eq(0).type(groupName);
|
|
cy.wait(timeout);
|
|
cy.get(usersSelector.groupSelectInput).eq(0).check();
|
|
};
|
|
|
|
export const updateUserGroup = (groupName) => {
|
|
cy.get(usersSelector.userActionButton).click();
|
|
cy.get(usersSelector.editUserDetailsButton).click();
|
|
selectGroup(groupName);
|
|
};
|
|
|
|
export const inviteUserWithUserGroups = (firstName, email, ...groupNames) => {
|
|
fillUserInviteForm(firstName, email);
|
|
|
|
cy.wait(2000);
|
|
|
|
cy.get("body").then(($body) => {
|
|
const selectDropdown = $body.find(usersSelector.groupSelector);
|
|
|
|
if (selectDropdown.length === 0) {
|
|
cy.get(usersSelector.groupSelector).click();
|
|
}
|
|
cy.get(usersSelector.groupSelector).eq(0).type(groupNames[0]);
|
|
cy.wait(1000);
|
|
cy.get(usersSelector.groupSelectInput).eq(0).check();
|
|
cy.wait(1000);
|
|
cy.get(usersSelector.groupSelector).eq(0).type(groupNames[1]);
|
|
cy.wait(1000);
|
|
cy.get(usersSelector.groupSelectInput).eq(0).check();
|
|
});
|
|
|
|
cy.get(usersSelector.buttonInviteUsers).click();
|
|
cy.verifyToastMessage(
|
|
commonSelectors.toastMessage,
|
|
usersText.userCreatedToast
|
|
);
|
|
|
|
cy.wait(1000);
|
|
fetchAndVisitInviteLink(email);
|
|
cy.wait(2000);
|
|
cy.clearAndType(onboardingSelectors.loginPasswordInput, "password");
|
|
// cy.intercept('GET', '/api/organizations').as('org')
|
|
cy.get(commonSelectors.signUpButton).click();
|
|
cy.wait(2000);
|
|
cy.get(commonSelectors.acceptInviteButton).click();
|
|
};
|
|
|
|
export const fetchAndVisitInviteLink = (
|
|
email,
|
|
workspaceName = "My workspace"
|
|
) => {
|
|
let invitationToken, organizationToken, workspaceId, userId;
|
|
|
|
cy.runSqlQueryOnDB(`select invitation_token from users where email='${email}';`)
|
|
.then((resp) => {
|
|
invitationToken = resp.rows[0]?.invitation_token;
|
|
return cy.runSqlQueryOnDB(
|
|
`select id from organizations where name='${workspaceName}';`
|
|
);
|
|
})
|
|
.then((resp) => {
|
|
workspaceId = resp.rows[0]?.id;
|
|
return cy.runSqlQueryOnDB(`select id from users where email='${email}';`);
|
|
})
|
|
.then((resp) => {
|
|
userId = resp.rows[0]?.id;
|
|
return cy.runSqlQueryOnDB(
|
|
`select invitation_token from organization_users where user_id='${userId}';`
|
|
);
|
|
})
|
|
.then((resp) => {
|
|
organizationToken =
|
|
resp.rows?.[1]?.invitation_token || resp.rows?.[0]?.invitation_token;
|
|
const url = `/invitations/${invitationToken}/workspaces/${organizationToken}?oid=${workspaceId}`;
|
|
|
|
cy.apiLogout();
|
|
cy.wait(200);
|
|
cy.visit(url);
|
|
});
|
|
};
|
|
|
|
export const fetchAndVisitInviteLinkViaMH = (email) => {
|
|
cy.mhGetMailsByRecipient(email).then((mails) => {
|
|
expect(mails).to.have.length.greaterThan(0);
|
|
const lastMail = mails[mails.length - 1];
|
|
const mailContent = lastMail && lastMail.Content ? lastMail.Content : {};
|
|
const mailBody = mailContent.Body || mailContent.Html || "";
|
|
|
|
// Clean the email body by removing quoted-printable encoding and HTML entities
|
|
let cleanedBody = mailBody
|
|
.replace(/=\r?\n/g, "") // Remove quoted-printable line breaks (= at end of line)
|
|
.replace(/=3D/g, "=") // Decode =3D back to =
|
|
.replace(/"/g, '"')
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/&/g, "&");
|
|
|
|
// Extract URL from href attribute or plain text
|
|
let inviteUrl = "";
|
|
|
|
// Try to find URL in href attribute first
|
|
const hrefMatch = cleanedBody.match(
|
|
/href=["']?(http[^"'\s>]*invitation[^"'\s>]*)/i
|
|
);
|
|
if (hrefMatch) {
|
|
inviteUrl = hrefMatch[1];
|
|
} else {
|
|
// Fallback: look for URL in plain text
|
|
const urlMatch = cleanedBody.match(
|
|
/https?:\/\/[^\s"'<>]*invitation[s]?[^\s"'<>]*/i
|
|
);
|
|
inviteUrl = urlMatch ? urlMatch[0] : "";
|
|
}
|
|
|
|
expect(inviteUrl).to.not.be.empty;
|
|
cy.log("Found invite URL: " + inviteUrl);
|
|
cy.visit(inviteUrl);
|
|
});
|
|
};
|
|
|
|
export const inviteUserWithUserRole = (firstName, email, role) => {
|
|
fillUserInviteForm(firstName, email);
|
|
|
|
cy.wait(2000);
|
|
|
|
cy.get("body").then(($body) => {
|
|
const selectDropdown = $body.find(usersSelector.groupSelector);
|
|
|
|
if (selectDropdown.length === 0) {
|
|
cy.get(usersSelector.groupSelector).click();
|
|
}
|
|
cy.get(usersSelector.groupSelector).eq(0).type(role);
|
|
cy.wait(1000);
|
|
cy.get(usersSelector.groupSelectInput).eq(0).check();
|
|
cy.wait(1000);
|
|
});
|
|
|
|
cy.get(usersSelector.buttonInviteUsers).click();
|
|
cy.verifyToastMessage(
|
|
commonSelectors.toastMessage,
|
|
usersText.userCreatedToast
|
|
);
|
|
|
|
cy.wait(1000);
|
|
fetchAndVisitInviteLink(email);
|
|
cy.clearAndType(onboardingSelectors.loginPasswordInput, "password");
|
|
cy.get(commonSelectors.signUpButton).click();
|
|
cy.wait(2000);
|
|
cy.get(commonSelectors.acceptInviteButton).click();
|
|
cy.get(commonSelectors.homePageLogo, { timeout: 10000 }).should("be.visible");
|
|
};
|
|
export const verifyUserStatusAndMetadata = (
|
|
email,
|
|
expectedStatus = usersText.activeStatus,
|
|
expectedMetadata = "{..}"
|
|
) => {
|
|
common.searchUser(email);
|
|
cy.contains("td", email)
|
|
.parent()
|
|
.within(() => {
|
|
cy.get("td small").should("have.text", expectedStatus);
|
|
cy.get("td[data-name='meta-header'] .metadata")
|
|
.should("be.visible")
|
|
.and("have.text", expectedMetadata);
|
|
});
|
|
};
|
|
|
|
export const openEditUserDetails = (
|
|
email,
|
|
activeStatusText = usersText.activeStatus,
|
|
expectedMetadata = "{..}"
|
|
) => {
|
|
common.navigateToManageUsers();
|
|
|
|
verifyUserStatusAndMetadata(email, activeStatusText, expectedMetadata);
|
|
|
|
navigateToEditUser(email);
|
|
};
|
|
|
|
export const navigateToEditUser = (email) => {
|
|
cy.contains("td", email)
|
|
.parent()
|
|
.within(() => {
|
|
cy.get('[data-cy="user-actions-button"]').click();
|
|
});
|
|
cy.get('[data-cy="edit-user-details-button"]')
|
|
.verifyVisibleElement("have.text", "Edit user details")
|
|
.click();
|
|
};
|
|
|
|
export const cleanAllUsers = () => {
|
|
let authHeaders;
|
|
const emailsToDelete = new Set();
|
|
const devEmail = "[email protected]";
|
|
|
|
const collectEmails = (users = []) => {
|
|
users.forEach(({ email }) => {
|
|
if (!email) {
|
|
return;
|
|
}
|
|
|
|
const normalized = String(email).toLowerCase();
|
|
|
|
if (normalized !== devEmail) {
|
|
emailsToDelete.add(email);
|
|
}
|
|
});
|
|
};
|
|
|
|
const fetchUsersByPage = (page = 1) => {
|
|
return cy
|
|
.request({
|
|
method: "GET",
|
|
url: `${Cypress.env("server_host")}/api/users`,
|
|
headers: authHeaders,
|
|
qs: {
|
|
page,
|
|
searchText: "",
|
|
status: "",
|
|
},
|
|
log: false,
|
|
})
|
|
.then(({ body }) => {
|
|
collectEmails(body?.users ?? []);
|
|
|
|
const totalPages = Number(body?.meta?.total_pages) || 1;
|
|
return { totalPages };
|
|
});
|
|
};
|
|
|
|
return cy
|
|
.getAuthHeaders()
|
|
.then((headers) => {
|
|
authHeaders = headers;
|
|
})
|
|
.then(() => fetchUsersByPage(1))
|
|
.then(({ totalPages }) => {
|
|
if (totalPages <= 1) {
|
|
return;
|
|
}
|
|
|
|
const remainingPages = Array.from(
|
|
{ length: totalPages - 1 },
|
|
(_, index) => index + 2
|
|
);
|
|
return cy
|
|
.wrap(remainingPages)
|
|
.each((pageNumber) => fetchUsersByPage(pageNumber));
|
|
})
|
|
.then(() => {
|
|
if (!emailsToDelete.size) {
|
|
return cy.log("No users to clean up");
|
|
}
|
|
|
|
const deletableEmails = Array.from(emailsToDelete);
|
|
|
|
cy.log(`Batch deleting ${deletableEmails.length} users...`);
|
|
|
|
const sanitizedEmails = deletableEmails.map((email) =>
|
|
email.replace(/'/g, "''")
|
|
);
|
|
const emailsArrayLiteral = `ARRAY['${sanitizedEmails.join("','")}']::text[]`;
|
|
|
|
return cy.runSqlQueryOnDB(`CALL delete_users(${emailsArrayLiteral});`);
|
|
});
|
|
};
|
|
|
|
export const apiArchiveUnarchiveUser = (
|
|
email,
|
|
action,
|
|
workspaceId = Cypress.env("workspaceId")
|
|
) => {
|
|
return cy.apiGetUserDetails().then((res) => {
|
|
const resp = res?.body ?? res;
|
|
cy.log("org-users response: " + JSON.stringify(resp));
|
|
|
|
const users = Array.isArray(resp?.users) ? resp.users : [];
|
|
const orgUser = users.find((u) => u.email === email);
|
|
|
|
if (!orgUser?.id) {
|
|
throw new Error(`Organization user record not found for email: ${email}`);
|
|
}
|
|
|
|
const organizationId = orgUser.organization_id || workspaceId;
|
|
|
|
return cy
|
|
.getAuthHeaders()
|
|
.then((headers) =>
|
|
cy.request({
|
|
method: "POST",
|
|
url: `${Cypress.env("server_host")}/api/organization-users/${orgUser.id}/${action}`,
|
|
headers,
|
|
body: { organizationId },
|
|
log: false,
|
|
})
|
|
)
|
|
.then((response) => {
|
|
expect(response.status).to.be.oneOf([200, 201, 204]);
|
|
Cypress.log({
|
|
name: "Status Updated",
|
|
message: `User ${email} ${action}d`,
|
|
});
|
|
return response;
|
|
});
|
|
});
|
|
};
|