Merge branch 'main' into invite-flow-part1/platform14

This commit is contained in:
Muhsin Shah 2024-03-14 09:49:54 +05:30
commit d2bc9dd1e5
29 changed files with 467 additions and 305 deletions

View file

@ -1 +1 @@
2.32.3 2.32.5

View file

@ -76,8 +76,8 @@ module.exports = defineConfig({
experimentalRunAllSpecs: true, experimentalRunAllSpecs: true,
baseUrl: "http://localhost:8082", baseUrl: "http://localhost:8082",
specPattern: [ specPattern: [
"cypress/e2e/happypath/appbuilder/commonTestcases/**/*.cy.js", "cypress/e2e/happyPath/appbuilder/commonTestcases/**/*.cy.js",
"cypress/e2e/happypath/appbuilder/ceTestcases/**/*.cy.js" "cypress/e2e/happyPath/appbuilder/ceTestcases/**/*.cy.js"
], ],
numTestsKeptInMemory: 1, numTestsKeptInMemory: 1,
redirectionLimit: 7, redirectionLimit: 7,

View file

@ -76,8 +76,8 @@ module.exports = defineConfig({
experimentalRunAllSpecs: true, experimentalRunAllSpecs: true,
baseUrl: "http://localhost:8082", baseUrl: "http://localhost:8082",
specPattern: [ specPattern: [
"cypress/e2e/happypath/marketplace/commonTestcases/**/*.cy.js", "cypress/e2e/happyPath/marketplace/commonTestcases/**/*.cy.js",
"cypress/e2e/happypath/marketplace/ceTestcases/**/*.cy.js" "cypress/e2e/happyPath/marketplace/ceTestcases/**/*.cy.js"
], ],
numTestsKeptInMemory: 1, numTestsKeptInMemory: 1,
redirectionLimit: 7, redirectionLimit: 7,

View file

@ -76,8 +76,8 @@ module.exports = defineConfig({
experimentalRunAllSpecs: true, experimentalRunAllSpecs: true,
baseUrl: "http://localhost:8082", baseUrl: "http://localhost:8082",
specPattern: [ specPattern: [
"cypress/e2e/happypath/platform/commonTestcases/**/*.cy.js", "cypress/e2e/happyPath/platform/commonTestcases/**/*.cy.js",
"cypress/e2e/happypath/platform/ceTestcases/**/*.cy.js" "cypress/e2e/happyPath/platform/ceTestcases/**/*.cy.js"
], ],
numTestsKeptInMemory: 1, numTestsKeptInMemory: 1,
redirectionLimit: 15, redirectionLimit: 15,

View file

@ -83,7 +83,7 @@ export const commonSelectors = {
workspaceSettings: '[data-cy="workspace-settings"]', workspaceSettings: '[data-cy="workspace-settings"]',
manageUsersOption: '[data-cy="users-list-item"]', manageUsersOption: '[data-cy="users-list-item"]',
manageGroupsOption: '[data-cy="groups-list-item"]', manageGroupsOption: '[data-cy="groups-list-item"]',
manageSSOOption: '[data-cy="sso-list-item"]', manageSSOOption: '[data-cy="workspace-login-list-item"]',
workspaceVariableOption: '[data-cy="workspace-variables-list-item"]', workspaceVariableOption: '[data-cy="workspace-variables-list-item"]',
clearFilterButton: '[data-cy="clear-filter-button"]', clearFilterButton: '[data-cy="clear-filter-button"]',
userStatusSelect: '[data-cy="user-status-select-continer"]', userStatusSelect: '[data-cy="user-status-select-continer"]',
@ -257,6 +257,7 @@ export const commonSelectors = {
}, },
defaultModalTitle: '[data-cy="modal-title"]', defaultModalTitle: '[data-cy="modal-title"]',
workspaceConstantsIcon: '[data-cy="icon-workspace-constants"]', workspaceConstantsIcon: '[data-cy="icon-workspace-constants"]',
confirmationButton: '[data-cy="confirmation-button"]',
}; };
export const commonWidgetSelector = { export const commonWidgetSelector = {

View file

@ -1,7 +1,6 @@
export const ssoSelector = { export const ssoSelector = {
pagetitle: "[data-cy=manage-sso-page-title]", pagetitle: "[data-cy=manage-sso-page-title]",
generalSettingsElements: { workspaceLoginPage: {
generalSettings: '[data-cy="general-settings-list-item"]',
enableSignupLabel: '[data-cy="enable-sign-up-label"]', enableSignupLabel: '[data-cy="enable-sign-up-label"]',
helperText: "[data-cy=enable-sign-up-helper-text]", helperText: "[data-cy=enable-sign-up-helper-text]",
allowDefaultSSOLabel: '[data-cy="allow-default-sso-label"]', allowDefaultSSOLabel: '[data-cy="allow-default-sso-label"]',
@ -10,16 +9,22 @@ export const ssoSelector = {
allowedDomainHelperText: '[data-cy="allowed-domain-helper-text"]', allowedDomainHelperText: '[data-cy="allowed-domain-helper-text"]',
workspaceLoginUrl: '[data-cy="workspace-login-url-label"]', workspaceLoginUrl: '[data-cy="workspace-login-url-label"]',
workspaceLoginHelpText: '[data-cy="workspace-login-help-text"]', workspaceLoginHelpText: '[data-cy="workspace-login-help-text"]',
ssoHeader: '[data-cy="sso-header"]',
instanceSSOHelperText: '[data-cy="instance-sso-helper-text"]',
googleLabel: '[data-cy="google-label"]',
githubLabel: '[data-cy="github-label"]',
defaultSSO: '[data-cy="instance-sso-card"]',
}, },
cardTitle: "[data-cy=card-title]", cardTitle: "[data-cy=card-title]",
enableSignUpToggle: '[data-cy="enable-sign-up-toggle"]', enableSignUpToggle: '[data-cy="enable-sign-up-toggle"]',
allowDefaultSSOToggle: '[data-cy="allow-default-sso-toggle"]', allowDefaultSSOToggle:
'[style="padding-left: 0px; margin-bottom: 1px;"] > .switch > .slider',
defaultSSOImage: '[data-cy="default-sso-status-image"]', defaultSSOImage: '[data-cy="default-sso-status-image"]',
allowedDomainInput: "[data-cy=allowed-domain-input]", allowedDomainInput: '[data-cy="allowed-domains"]',
workspaceLoginUrl: '[data-cy="workspace-login-url"]', workspaceLoginUrl: '[data-cy="workspace-login-url"]',
cancelButton: "[data-cy=cancel-button]", cancelButton: "[data-cy=cancel-button]",
saveButton: "[data-cy=save-button]", saveButton: "[data-cy=save-button]",
google: '[data-cy="google-list-item"]', google: '[data-cy="google-sso-card"]',
googleEnableToggle: '[data-cy="google-enable-toggle"]', googleEnableToggle: '[data-cy="google-enable-toggle"]',
statusLabel: "[data-cy=status-label]", statusLabel: "[data-cy=status-label]",
clientIdLabel: "[data-cy=client-id-label]", clientIdLabel: "[data-cy=client-id-label]",
@ -29,9 +34,9 @@ export const ssoSelector = {
googleTile: '[data-cy="google-sign-in-text"]', googleTile: '[data-cy="google-sign-in-text"]',
googleIcon: "[data-cy=google-sso-icon]", googleIcon: "[data-cy=google-sso-icon]",
googleSSOText: "[data-cy=google-sso-text]", googleSSOText: "[data-cy=google-sso-text]",
git: '[data-cy="github-list-item"]', git: '[data-cy="github-sso-card"]',
gitEnableToggle: '[data-cy="github-toggle-input"]', gitEnableToggle: '[data-cy="github-toggle-input"]',
githubLabel: '[data-cy="github-toggle-label"]', githubLabel: '[data-cy="github-label"]',
clientSecretLabel: "[data-cy=client-secret-label]", clientSecretLabel: "[data-cy=client-secret-label]",
encriptedLabel: "[data-cy=encripted-label]", encriptedLabel: "[data-cy=encripted-label]",
clientSecretInput: "[data-cy=client-secret-input]", clientSecretInput: "[data-cy=client-secret-input]",
@ -52,4 +57,6 @@ export const ssoSelector = {
passwordLoginToggleLbale: '[data-cy="label-password-login"]', passwordLoginToggleLbale: '[data-cy="label-password-login"]',
alertText: '[data-cy="alert-text"]', alertText: '[data-cy="alert-text"]',
disablePasswordHelperText: '[data-cy="disable-password-helper-text"]', disablePasswordHelperText: '[data-cy="disable-password-helper-text"]',
defaultGoogle: '[data-cy="dropdown-options-google"]',
defaultGithub: '[data-cy="dropdown-options-git"]',
}; };

View file

@ -1,7 +1,6 @@
export const ssoText = { export const ssoText = {
pagetitle: " SSO", pagetitle: " Workspace login",
generalSettingsElements: { workspaceLoginPage: {
generalSettings: "General Settings",
enableSignupLabel: "Enable Signup", enableSignupLabel: "Enable Signup",
helperText: "New account will be created for user's first time SSO sign in", helperText: "New account will be created for user's first time SSO sign in",
allowDefaultSSOLabel: "Allow default SSO", allowDefaultSSOLabel: "Allow default SSO",
@ -12,34 +11,39 @@ export const ssoText = {
"Support multiple domains. Enter domain names separated by comma. example: tooljet.com,tooljet.io,yourorganization.com", "Support multiple domains. Enter domain names separated by comma. example: tooljet.com,tooljet.io,yourorganization.com",
workspaceLoginUrl: "Login URL", workspaceLoginUrl: "Login URL",
workspaceLoginHelpText: "Use this URL to login directly to this workspace", workspaceLoginHelpText: "Use this URL to login directly to this workspace",
ssoHeader: "SSO",
instanceSSOHelperText: "Display default SSO for workspace URL login",
googleLabel: "Google",
githubLabel: "GitHub",
defaultSSO: "Default SSO",
}, },
cancelButton: "Cancel", cancelButton: "Cancel",
saveButton: "Save changes", saveButton: "Save changes",
allowedDomain: "tooljet.io,gmail.com", allowedDomain: "tooljet.io,gmail.com",
ssoToast: "updated sso configurations", ssoToast: "Organization settings have been updated",
ssoToast2: "updated SSO configurations", ssoToast2: "updated SSO configurations",
googleTitle: "Google", googleTitle: "Google",
enabledLabel: "Enabled", enabledLabel: "Enabled",
googleEnabledToast: "Enabled Google SSO", googleSSOToast: "Saved Google SSO configurations",
disabledLabel: "Disabled", disabledLabel: "Disabled",
googleDisableToast: "Disabled Google SSO", googleDisableToast: "Disabled Google SSO",
googleSSOText: "Sign in with Google", googleSSOText: "Sign in with Google",
clientIdLabel: "Client Id", clientIdLabel: "Client ID",
redirectUrlLabel: "Redirect URL", redirectUrlLabel: "Redirect URL",
clientId: "24567098-mklj8t20za1smb2if.apps.googleusercontent.com", clientId: "24567098-mklj8t20za1smb2if.apps.googleusercontent.com",
testClientId: "12345-client-id-.apps.googleusercontent.com", testClientId: "12345-client-id-.apps.googleusercontent.com",
gitTitle: "GitHub", gitTitle: "GitHub",
clientSecretLabel: "Client Secret", clientSecretLabel: "Client secret",
encriptedLabel: "Encrypted", encriptedLabel: "Encrypted",
gitEnabledToast: "Enabled GitHub SSO", gitEnabledToast: "Enabled GitHub SSO",
gitDisabledToast: "Disabled GitHub SSO", gitSSOToast: "Saved Git SSO configurations",
gitSignInText: "Sign in with GitHub", gitSignInText: "Sign in with GitHub",
passwordTitle: "Password Login", passwordTitle: "Password Login",
passwordEnabledToast: "Enabled Password login", passwordEnabledToast: "Enabled Password login",
passwordDisabledToast: "Disabled Password login", passwordDisabledToast: "Password login disabled successfully!",
passwordDisableWarning: passwordDisableWarning:
"Users wont be able to login via username and password if password login is disabled. Please make sure that you have setup other authentication methods before disabling password login, do you want to continue?", "Disable password login only if you have configured SSO or else you will get locked out.",
hostNameLabel: "Host Name", hostNameLabel: "Host name",
hostNameHelpText: "Required if GitHub is self hosted", hostNameHelpText: "Required if GitHub is self hosted",
hostName: "Tooljet", hostName: "Tooljet",
signInHeader: "Sign in", signInHeader: "Sign in",
@ -51,8 +55,8 @@ export const ssoText = {
gitSignUpText: "Sign up with GitHub", gitSignUpText: "Sign up with GitHub",
gitUserStatusToast: gitUserStatusToast:
"GitHub login failed - User does not exist in the workspace", "GitHub login failed - User does not exist in the workspace",
passwordLoginToggleLbale: "Password login ", passwordLoginToggleLbale: "Password login",
alertText: "Danger zone", alertText: "Danger zone",
disablePasswordHelperText: disablePasswordHelperText:
"Disable password login only if your SSO is configured otherwise you will get logged out.", "Disable password login only if your SSO is configured otherwise you will get locked out",
}; };

View file

@ -1,5 +1,5 @@
import { commonSelectors } from "../../constants/selectors/common"; import { commonSelectors } from "Selectors/common";
import { commonText } from "../../constants/texts/common"; import { commonText } from "Texts/common";
import { fake } from "Fixtures/fake"; import { fake } from "Fixtures/fake";
import { addNewUser } from "Support/utils/onboarding"; import { addNewUser } from "Support/utils/onboarding";
import { logout } from "Support/utils/common"; import { logout } from "Support/utils/common";

View file

@ -11,10 +11,11 @@ describe("Manage SSO for multi workspace", () => {
const envVar = Cypress.env("environment"); const envVar = Cypress.env("environment");
beforeEach(() => { beforeEach(() => {
cy.defaultWorkspaceLogin(); cy.defaultWorkspaceLogin();
SSO.setSSOStatus("My workspace", "google", false);
SSO.setSSOStatus("My workspace", "git", false);
}); });
it("Should verify General settings page elements", () => { it("Should verify General settings page elements", () => {
common.navigateToManageSSO(); common.navigateToManageSSO();
cy.get(commonSelectors.breadcrumbTitle).should(($el) => { cy.get(commonSelectors.breadcrumbTitle).should(($el) => {
expect($el.contents().first().text().trim()).to.eq( expect($el.contents().first().text().trim()).to.eq(
commonText.breadcrumbworkspaceSettingTitle commonText.breadcrumbworkspaceSettingTitle
@ -27,14 +28,12 @@ describe("Manage SSO for multi workspace", () => {
cy.get(ssoSelector.cardTitle).verifyVisibleElement( cy.get(ssoSelector.cardTitle).verifyVisibleElement(
"have.text", "have.text",
ssoText.generalSettingsElements.generalSettings "Workspace login"
); );
for (const elements in ssoSelector.generalSettingsElements) { for (const elements in ssoSelector.generalSettingsElements) {
cy.get( cy.get(ssoSelector.workspaceLoginPage[elements]).verifyVisibleElement(
ssoSelector.generalSettingsElements[elements]
).verifyVisibleElement(
"have.text", "have.text",
ssoText.generalSettingsElements[elements] ssoText.workspaceLoginPage[elements]
); );
} }
cy.get(ssoSelector.enableSignUpToggle).should("be.visible"); cy.get(ssoSelector.enableSignUpToggle).should("be.visible");
@ -51,12 +50,6 @@ describe("Manage SSO for multi workspace", () => {
ssoText.saveButton ssoText.saveButton
); );
SSO.generalSettings();
cy.get(ssoSelector.alertText).verifyVisibleElement(
"have.text",
ssoText.alertText
);
cy.get(ssoSelector.passwordEnableToggle).should("be.visible"); cy.get(ssoSelector.passwordEnableToggle).should("be.visible");
cy.get(ssoSelector.passwordLoginToggleLbale).verifyVisibleElement( cy.get(ssoSelector.passwordLoginToggleLbale).verifyVisibleElement(
"have.text", "have.text",
@ -67,35 +60,32 @@ describe("Manage SSO for multi workspace", () => {
ssoText.disablePasswordHelperText ssoText.disablePasswordHelperText
); );
SSO.passwordPageElements(); SSO.generalSettings();
}); });
it("Should verify Google SSO page elements", () => { it("Should verify Google SSO page elements", () => {
common.navigateToManageSSO(); common.navigateToManageSSO();
cy.get(ssoSelector.google).should("be.visible").click(); cy.get(ssoSelector.google).should("be.visible").click();
cy.get(ssoSelector.cardTitle).verifyVisibleElement( cy.get(ssoSelector.cardTitle)
"have.text", .eq(1)
ssoText.googleTitle .verifyVisibleElement("have.text", ssoText.googleTitle);
);
cy.get(ssoSelector.googleEnableToggle).should("be.visible"); cy.get(ssoSelector.googleEnableToggle).should("be.visible");
cy.get(ssoSelector.clientIdLabel).verifyVisibleElement( cy.get(ssoSelector.clientIdLabel).verifyVisibleElement(
"have.text", "have.text",
ssoText.clientIdLabel ssoText.clientIdLabel
); );
cy.get(ssoSelector.clientIdInput).should("be.visible"); cy.get(ssoSelector.clientIdInput).should("be.visible");
cy.get(ssoSelector.cancelButton).verifyVisibleElement( cy.get(ssoSelector.cancelButton)
"have.text", .eq(1)
ssoText.cancelButton .verifyVisibleElement("have.text", ssoText.cancelButton);
); cy.get(ssoSelector.saveButton)
cy.get(ssoSelector.saveButton).verifyVisibleElement( .eq(1)
"have.text", .verifyVisibleElement("have.text", ssoText.saveButton);
ssoText.saveButton
);
SSO.googleSSOPageElements(); SSO.googleSSOPageElements();
SSO.disableDefaultSSO(); SSO.defaultSSO("My workspace", false);
SSO.visitWorkspaceLoginPage(); cy.logoutApi();
cy.visit("/login/my-workspace");
cy.get(ssoSelector.googleIcon).should("be.visible"); cy.get(ssoSelector.googleIcon).should("be.visible");
cy.get(ssoSelector.googleSSOText).verifyVisibleElement( cy.get(ssoSelector.googleSSOText).verifyVisibleElement(
"have.text", "have.text",
@ -104,6 +94,8 @@ describe("Manage SSO for multi workspace", () => {
}); });
it("Should verify Git SSO page elements", () => { it("Should verify Git SSO page elements", () => {
SSO.defaultSSO("My workspace", true);
common.navigateToManageSSO(); common.navigateToManageSSO();
cy.get(ssoSelector.git).should("be.visible").click(); cy.get(ssoSelector.git).should("be.visible").click();
@ -139,27 +131,27 @@ describe("Manage SSO for multi workspace", () => {
ssoText.encriptedLabel ssoText.encriptedLabel
); );
cy.get(ssoSelector.clientSecretInput).should("be.visible"); cy.get(ssoSelector.clientSecretInput).should("be.visible");
cy.get(ssoSelector.cancelButton).verifyVisibleElement( cy.get(ssoSelector.cancelButton)
"have.text", .eq(1)
ssoText.cancelButton .verifyVisibleElement("have.text", ssoText.cancelButton);
); cy.get(ssoSelector.saveButton)
cy.get(ssoSelector.saveButton).verifyVisibleElement( .eq(1)
"have.text", .verifyVisibleElement("have.text", ssoText.saveButton);
ssoText.saveButton
);
SSO.gitSSOPageElements(); SSO.gitSSOPageElements();
SSO.visitWorkspaceLoginPage(); SSO.defaultSSO("My workspace", false);
cy.logoutApi();
cy.visit("/login/my-workspace");
cy.get(ssoSelector.googleIcon).should("be.visible"); cy.get(ssoSelector.gitIcon).should("be.visible");
cy.get(ssoSelector.googleSSOText).verifyVisibleElement( cy.get(ssoSelector.gitSignInText).verifyVisibleElement(
"have.text", "have.text",
ssoText.googleSSOText ssoText.gitSignInText
); );
}); });
if (envVar === "Community") { if (envVar === "Community") {
it("Should verify the workspace login page", () => { it.skip("Should verify the workspace login page", () => {
data.workspaceName = fake.companyName.toLowerCase(); data.workspaceName = fake.companyName.toLowerCase();
cy.apiLogin(); cy.apiLogin();
cy.apiCreateWorkspace(data.workspaceName, data.workspaceName); cy.apiCreateWorkspace(data.workspaceName, data.workspaceName);

View file

@ -9,7 +9,7 @@ import { dashboardSelector } from "Selectors/dashboard";
import { updateWorkspaceName } from "Support/utils/userPermissions"; import { updateWorkspaceName } from "Support/utils/userPermissions";
import { groupsSelector } from "Selectors/manageGroups"; import { groupsSelector } from "Selectors/manageGroups";
import { groupsText } from "Texts/manageGroups"; import { groupsText } from "Texts/manageGroups";
import { addNewUser } from "../../support/utils/onboarding"; import { addNewUser } from "Support/utils/onboarding";
const data = {}; const data = {};
data.groupName = fake.firstName.replaceAll("[^A-Za-z]", ""); data.groupName = fake.firstName.replaceAll("[^A-Za-z]", "");

View file

@ -275,3 +275,11 @@ export const releaseApp = () => {
cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released"); cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released");
cy.wait(1000); cy.wait(1000);
}; };
export const verifyTooltipDisabled = (selector, message) => {
cy.get(selector)
.trigger("mouseover", { force: true })
.then(() => {
cy.get(".tooltip-inner").last().should("have.text", message);
});
};

View file

@ -6,207 +6,190 @@ import { commonText } from "Texts/common";
import { dashboardSelector } from "Selectors/dashboard"; import { dashboardSelector } from "Selectors/dashboard";
export const generalSettings = () => { export const generalSettings = () => {
cy.get(ssoSelector.enableSignUpToggle).then(($el) => { cy.get(ssoSelector.enableSignUpToggle).check();
if ($el.is(":checked")) { cy.get(ssoSelector.cancelButton).click();
cy.get(ssoSelector.enableSignUpToggle).uncheck(); cy.get(ssoSelector.enableSignUpToggle).should("not.be.checked");
cy.get(ssoSelector.cancelButton).click(); cy.get(ssoSelector.enableSignUpToggle).check();
cy.get(ssoSelector.enableSignUpToggle).should("be.checked"); cy.get(ssoSelector.saveButton).click();
} else { cy.get(ssoSelector.enableSignUpToggle).should("be.checked");
cy.get(ssoSelector.enableSignUpToggle).check();
cy.get(ssoSelector.cancelButton).click(); cy.get(ssoSelector.enableSignUpToggle).uncheck();
cy.get(ssoSelector.enableSignUpToggle).should("not.be.checked"); cy.get(ssoSelector.saveButton).click();
cy.get(ssoSelector.enableSignUpToggle).check(); cy.get(ssoSelector.enableSignUpToggle).should("not.be.checked");
}
cy.get(ssoSelector.allowDefaultSSOToggle).then(($el) => { cy.get(ssoSelector.workspaceLoginPage.defaultSSO).click();
if ($el.is(":checked")) { cy.get(ssoSelector.defaultGoogle).verifyVisibleElement("have.text", "Google");
cy.get(ssoSelector.allowDefaultSSOToggle).uncheck(); cy.get(ssoSelector.defaultGithub).verifyVisibleElement("have.text", "Github");
cy.get(ssoSelector.cancelButton).click();
cy.get(ssoSelector.allowDefaultSSOToggle).should("not.be.checked"); cy.clearAndType(ssoSelector.allowedDomainInput, ssoText.allowedDomain);
} else { cy.get(ssoSelector.saveButton).click();
cy.get(ssoSelector.allowDefaultSSOToggle).check(); cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.ssoToast);
cy.get(ssoSelector.cancelButton).click();
cy.get(ssoSelector.allowDefaultSSOToggle).should("be.checked"); cy.get(ssoSelector.passwordEnableToggle).uncheck();
cy.get(ssoSelector.allowDefaultSSOToggle).check(); cy.get(commonSelectors.modalComponent).should("be.visible");
} cy.get(commonSelectors.modalMessage).verifyVisibleElement(
}); "have.text",
cy.clearAndType(ssoSelector.allowedDomainInput, ssoText.allowedDomain); ssoText.passwordDisableWarning
cy.get(ssoSelector.saveButton).click(); );
cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.ssoToast); cy.get(commonSelectors.cancelButton).eq(1).click();
}); cy.get(ssoSelector.passwordEnableToggle).uncheck();
cy.get(commonSelectors.confirmationButton).click();
cy.verifyToastMessage(
commonSelectors.toastMessage,
ssoText.passwordDisabledToast
);
cy.get(ssoSelector.passwordEnableToggle).check();
cy.get(commonSelectors.saveButton).click();
cy.get(ssoSelector.allowDefaultSSOToggle).click();
cy.get(ssoSelector.passwordEnableToggle).should("be.disabled");
common.verifyTooltipDisabled(
ssoSelector.passwordEnableToggle,
"Password login cannot be disabled unless SSO is configured"
);
cy.get(ssoSelector.allowDefaultSSOToggle).click();
}; };
export const googleSSOPageElements = () => { export const googleSSOPageElements = () => {
cy.get(ssoSelector.googleEnableToggle).then(($el) => { cy.get(ssoSelector.googleEnableToggle).click();
if ($el.is(":checked")) { cy.get(ssoSelector.saveButton).eq(1).click();
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.enabledLabel
);
cy.get(ssoSelector.googleEnableToggle).uncheck();
cy.verifyToastMessage(
commonSelectors.toastMessage,
ssoText.googleDisableToast
);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.disabledLabel
);
cy.get(ssoSelector.googleEnableToggle).check();
cy.verifyToastMessage(
commonSelectors.toastMessage,
ssoText.googleEnabledToast
);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.enabledLabel
);
} else {
cy.get(ssoSelector.googleEnableToggle).check();
cy.verifyToastMessage(
commonSelectors.toastMessage,
ssoText.googleEnabledToast
);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.enabledLabel
);
cy.get(ssoSelector.googleEnableToggle).uncheck(); cy.get('[data-cy="modal-title"]').verifyVisibleElement(
cy.verifyToastMessage( "have.text",
commonSelectors.toastMessage, "Enable Google"
ssoText.googleDisableToast );
); cy.get('[data-cy="modal-close-button"]').should("be.visible");
cy.get(ssoSelector.statusLabel).verifyVisibleElement( cy.get('[data-cy="modal-message"]').verifyVisibleElement(
"have.text", "have.text",
ssoText.disabledLabel "Enabling Google at the workspace level will override any Google configurations set at the instance level."
); );
cy.get(ssoSelector.googleEnableToggle).check(); cy.get('[data-cy="confirmation-text"]').verifyVisibleElement(
cy.get(ssoSelector.statusLabel).verifyVisibleElement( "have.text",
"have.text", "Are you sure you want to continue?"
ssoText.enabledLabel );
); cy.get('[data-cy="cancel-button"]')
} .eq(2)
cy.clearAndType(ssoSelector.clientIdInput, ssoText.testClientId); .verifyVisibleElement("have.text", "Cancel");
cy.get(ssoSelector.cancelButton).click(); cy.get('[data-cy="enable-button"]').verifyVisibleElement(
cy.clearAndType(ssoSelector.clientIdInput, ssoText.clientId); "have.text",
cy.get(ssoSelector.saveButton).click(); "Enable"
cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.ssoToast2); );
cy.get(ssoSelector.clientIdInput).should("have.value", ssoText.clientId);
}); cy.get('[data-cy="cancel-button"]').eq(2).click();
cy.get(ssoSelector.googleEnableToggle).click();
cy.get(ssoSelector.saveButton).eq(1).click();
cy.get('[data-cy="enable-button"]').click();
cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.googleSSOToast);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.enabledLabel
);
cy.get('[data-cy="redirect-url-label"]').verifyVisibleElement(
"have.text",
ssoText.redirectUrlLabel
);
cy.get('[data-cy="redirect-url"]').should("be.visible");
cy.get('[data-cy="copy-icon"]').should("be.visible");
cy.get(ssoSelector.googleEnableToggle).click();
cy.get(ssoSelector.saveButton).eq(1).click();
cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.googleSSOToast);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.disabledLabel
);
cy.clearAndType(ssoSelector.clientIdInput, ssoText.testClientId);
cy.get(ssoSelector.cancelButton).eq(1).click();
cy.get(ssoSelector.google).click();
cy.get(ssoSelector.clientIdInput).should("have.value", "");
cy.get(ssoSelector.googleEnableToggle).click();
cy.clearAndType(ssoSelector.clientIdInput, ssoText.clientId);
cy.get(ssoSelector.saveButton).eq(1).click();
cy.get('[data-cy="enable-button"]').click();
cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.googleSSOToast);
cy.get(ssoSelector.clientIdInput).should("have.value", ssoText.clientId);
}; };
export const gitSSOPageElements = () => { export const gitSSOPageElements = () => {
cy.get(ssoSelector.gitEnableToggle).then(($el) => { cy.get(ssoSelector.gitEnableToggle).click();
if ($el.is(":checked")) {
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.enabledLabel
);
cy.get(ssoSelector.gitEnableToggle).uncheck();
cy.verifyToastMessage(
commonSelectors.toastMessage,
ssoText.gitDisabledToast
);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.disabledLabel
);
cy.get(ssoSelector.gitEnableToggle).check();
cy.verifyToastMessage(
commonSelectors.toastMessage,
ssoText.gitEnabledToast
);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.enabledLabel
);
} else {
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.disabledLabel
);
cy.get(ssoSelector.gitEnableToggle).check();
cy.verifyToastMessage(
commonSelectors.toastMessage,
ssoText.gitEnabledToast
);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.enabledLabel
);
cy.get(ssoSelector.gitEnableToggle).uncheck();
cy.verifyToastMessage(
commonSelectors.toastMessage,
ssoText.gitDisabledToast
);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.disabledLabel
);
cy.get(ssoSelector.gitEnableToggle).check();
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.enabledLabel
);
}
cy.clearAndType(ssoSelector.hostNameInput, ssoText.hostName);
cy.clearAndType(ssoSelector.clientIdInput, ssoText.clientId);
cy.clearAndType(ssoSelector.clientSecretInput, ssoText.testClientId);
cy.get(ssoSelector.saveButton).click();
cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.ssoToast2);
cy.get(ssoSelector.hostNameInput).should("have.value", ssoText.hostName);
cy.get(ssoSelector.clientIdInput).should("have.value", ssoText.clientId);
cy.get(ssoSelector.clientSecretInput).should(
"have.value",
ssoText.testClientId
);
});
};
export const passwordPageElements = () => { cy.get(ssoSelector.saveButton).eq(1).click();
cy.get(ssoSelector.passwordEnableToggle).then(($el) => {
if ($el.is(":checked")) {
cy.get(ssoSelector.passwordEnableToggle).uncheck();
cy.get(commonSelectors.modalComponent).should("be.visible");
cy.get(commonSelectors.modalMessage).verifyVisibleElement(
"have.text",
ssoText.passwordDisableWarning
);
cy.get(commonSelectors.buttonSelector("Yes")).click();
cy.verifyToastMessage(
commonSelectors.toastMessage,
ssoText.passwordDisabledToast
);
cy.get(ssoSelector.passwordEnableToggle).check(); cy.get('[data-cy="modal-title"]').verifyVisibleElement(
cy.verifyToastMessage( "have.text",
commonSelectors.toastMessage, "Enable Git"
ssoText.passwordEnabledToast );
); cy.get('[data-cy="modal-close-button"]').should("be.visible");
} else { cy.get('[data-cy="modal-message"]').verifyVisibleElement(
cy.get(ssoSelector.passwordEnableToggle).check(); "have.text",
cy.verifyToastMessage( "Enabling Git at the workspace level will override any Git configurations set at the instance level."
commonSelectors.toastMessage, );
ssoText.passwordEnabledToast cy.get('[data-cy="confirmation-text"]').verifyVisibleElement(
); "have.text",
"Are you sure you want to continue?"
);
cy.get('[data-cy="cancel-button"]')
.eq(2)
.verifyVisibleElement("have.text", "Cancel");
cy.get('[data-cy="enable-button"]').verifyVisibleElement(
"have.text",
"Enable"
);
cy.get(ssoSelector.passwordEnableToggle).uncheck(); cy.get('[data-cy="cancel-button"]').eq(2).click();
cy.verifyToastMessage( cy.get(ssoSelector.gitEnableToggle).click();
commonSelectors.toastMessage, cy.get(ssoSelector.saveButton).eq(1).click();
ssoText.passwordDisabledToast cy.get('[data-cy="enable-button"]').click();
);
cy.get(ssoSelector.passwordEnableToggle).check(); cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.gitSSOToast);
}
}); cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.enabledLabel
);
cy.get('[data-cy="redirect-url-label"]').verifyVisibleElement(
"have.text",
ssoText.redirectUrlLabel
);
cy.get('[data-cy="redirect-url"]').should("be.visible");
cy.get('[data-cy="copy-icon"]').should("be.visible");
cy.get(ssoSelector.gitEnableToggle).click();
cy.get(ssoSelector.saveButton).eq(1).click();
cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.gitSSOToast);
cy.get(ssoSelector.statusLabel).verifyVisibleElement(
"have.text",
ssoText.disabledLabel
);
cy.get(ssoSelector.gitEnableToggle).click();
cy.clearAndType(ssoSelector.hostNameInput, ssoText.hostName);
cy.clearAndType(ssoSelector.clientIdInput, ssoText.clientId);
cy.clearAndType(ssoSelector.clientSecretInput, ssoText.testClientId);
cy.get(ssoSelector.saveButton).eq(1).click();
cy.get('[data-cy="enable-button"]').click();
cy.verifyToastMessage(commonSelectors.toastMessage, ssoText.gitSSOToast);
cy.get(ssoSelector.hostNameInput).should("have.value", ssoText.hostName);
cy.get(ssoSelector.clientIdInput).should("have.value", ssoText.clientId);
cy.get(ssoSelector.clientSecretInput).should(
"have.value",
ssoText.testClientId
);
}; };
export const visitWorkspaceLoginPage = () => { export const visitWorkspaceLoginPage = () => {
cy.get(ssoSelector.generalSettingsElements.generalSettings).click();
cy.get(ssoSelector.workspaceLoginUrl).then(($temp) => { cy.get(ssoSelector.workspaceLoginUrl).then(($temp) => {
const url = $temp.text(); const url = $temp.text();
common.logout(); common.logout();
cy.wait(1000) cy.wait(1000);
cy.visit(url); cy.visit(url);
}); });
}; };
@ -264,7 +247,7 @@ export const workspaceLogin = (workspaceName) => {
cy.clearAndType(commonSelectors.workEmailInputField, "dev@tooljet.io"); cy.clearAndType(commonSelectors.workEmailInputField, "dev@tooljet.io");
cy.clearAndType(commonSelectors.passwordInputField, "password"); cy.clearAndType(commonSelectors.passwordInputField, "password");
cy.get(commonSelectors.loginButton).click(); cy.get(commonSelectors.loginButton).click();
cy.wait(2000) cy.wait(2000);
cy.get(commonSelectors.homePageLogo).should("be.visible"); cy.get(commonSelectors.homePageLogo).should("be.visible");
cy.get(commonSelectors.workspaceName).verifyVisibleElement( cy.get(commonSelectors.workspaceName).verifyVisibleElement(
"have.text", "have.text",
@ -576,3 +559,35 @@ export const updateId = () => {
sql: "update sso_configs set id='9628dee2-6fa9-4aca-9c98-ef950601c83e' where sso='git';", sql: "update sso_configs set id='9628dee2-6fa9-4aca-9c98-ef950601c83e' where sso='git';",
}); });
}; };
export const setSSOStatus = (workspaceName, ssoType, enabled) => {
let workspaceId;
cy.task("updateId", {
dbconfig: Cypress.env("app_db"),
sql: `SELECT id FROM organizations WHERE name = '${workspaceName}'`,
}).then((resp) => {
workspaceId = resp.rows[0].id;
cy.task("updateId", {
dbconfig: Cypress.env("app_db"),
sql: `SELECT * FROM sso_configs WHERE organization_id = '${workspaceId}' AND sso = '${ssoType}'`,
}).then((ssoConfigResp) => {
if (ssoConfigResp.rows.length > 0) {
cy.task("updateId", {
dbconfig: Cypress.env("app_db"),
sql: `UPDATE sso_configs SET enabled = ${enabled ? "true" : "false"
} WHERE organization_id = '${workspaceId}' AND sso = '${ssoType}'`,
});
}
});
});
};
export const defaultSSO = (workspaceName, enabled) => {
cy.task("updateId", {
dbconfig: Cypress.env("app_db"),
sql: `UPDATE organizations SET inherit_sso = ${enabled ? "true" : "false"
} WHERE name = '${workspaceName}'`,
});
};

View file

@ -1 +1 @@
2.32.3 2.32.5

View file

@ -1,4 +1,4 @@
import React, { useEffect, useState, useMemo, useContext, useRef, memo, useCallback } from 'react'; import React, { useEffect, useState, useMemo, useContext, memo } from 'react';
import { Button } from './Components/Button'; import { Button } from './Components/Button';
import { Image } from './Components/Image'; import { Image } from './Components/Image';
import { Text } from './Components/Text'; import { Text } from './Components/Text';
@ -42,7 +42,6 @@ import { Html } from './Components/Html';
import { ButtonGroup } from './Components/ButtonGroup'; import { ButtonGroup } from './Components/ButtonGroup';
import { CustomComponent } from './Components/CustomComponent/CustomComponent'; import { CustomComponent } from './Components/CustomComponent/CustomComponent';
import { VerticalDivider } from './Components/verticalDivider'; import { VerticalDivider } from './Components/verticalDivider';
import { PDF } from './Components/PDF';
import { ColorPicker } from './Components/ColorPicker'; import { ColorPicker } from './Components/ColorPicker';
import { KanbanBoard } from './Components/KanbanBoard/KanbanBoard'; import { KanbanBoard } from './Components/KanbanBoard/KanbanBoard';
import { Kanban } from './Components/Kanban/Kanban'; import { Kanban } from './Components/Kanban/Kanban';
@ -68,6 +67,7 @@ import { EditorContext } from '@/Editor/Context/EditorContextWrapper';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useCurrentState } from '@/_stores/currentStateStore'; import { useCurrentState } from '@/_stores/currentStateStore';
import { useAppInfo } from '@/_stores/appDataStore'; import { useAppInfo } from '@/_stores/appDataStore';
import { isPDFSupported } from '@/_stores/utils';
export const AllComponents = { export const AllComponents = {
Button, Button,
@ -112,7 +112,6 @@ export const AllComponents = {
ButtonGroup, ButtonGroup,
CustomComponent, CustomComponent,
VerticalDivider, VerticalDivider,
PDF,
ColorPicker, ColorPicker,
KanbanBoard, KanbanBoard,
Kanban, Kanban,
@ -124,6 +123,14 @@ export const AllComponents = {
BoundedBox, BoundedBox,
}; };
/**
* Conditionally importing PDF component since importing it breaks app in older versions of browsers.
* refer: https://github.com/wojtekmaj/react-pdf?tab=readme-ov-file#compatibility
**/
if (isPDFSupported()) {
AllComponents.PDF = await import('./Components/PDF').then((module) => module.PDF);
}
export const Box = memo( export const Box = memo(
({ ({
id, id,
@ -281,6 +288,9 @@ export const Box = memo(
...{ validationObject: component.definition.validation, currentState }, ...{ validationObject: component.definition.validation, currentState },
customResolveObjects: customResolvables, customResolveObjects: customResolvables,
}); });
const shouldHideWidget = component.component === 'PDF' && !isPDFSupported();
return ( return (
<OverlayTrigger <OverlayTrigger
placement={inCanvas ? 'auto' : 'top'} placement={inCanvas ? 'auto' : 'top'}
@ -315,7 +325,7 @@ export const Box = memo(
}} }}
role={preview ? 'BoxPreview' : 'Box'} role={preview ? 'BoxPreview' : 'Box'}
> >
{!resetComponent ? ( {!resetComponent && !shouldHideWidget ? (
<ComponentToRender <ComponentToRender
onComponentClick={onComponentClick} onComponentClick={onComponentClick}
onComponentOptionChanged={onComponentOptionChanged} onComponentOptionChanged={onComponentOptionChanged}

View file

@ -444,15 +444,37 @@ export function Table({
})); }));
tableData = useMemo(() => { tableData = useMemo(() => {
return tableData.map((row) => ({ return tableData.map((row) => {
...row, return {
...Object.fromEntries( ...row,
transformations.map((t) => [ ...Object.fromEntries(
t.key, transformations.map(({ key, transformation }) => {
resolveReferences(t.transformation, currentState, row[t.key], { cellValue: row[t.key], rowData: row }), const nestedKeys = key.includes('.') && key.split('.');
]) if (nestedKeys) {
), // Single-level nested property
})); const [nestedKey, subKey] = nestedKeys;
const nestedObject = row[nestedKey];
return [
nestedKey,
{
...nestedObject,
[subKey]: resolveReferences(transformation, currentState, row[key], {
cellValue: row?.[nestedKey]?.[subKey],
rowData: row,
}),
},
];
} else {
// Non-nested property
return [
key,
resolveReferences(transformation, currentState, row[key], { cellValue: row[key], rowData: row }),
];
}
})
),
};
});
}, [JSON.stringify([transformations, currentState, component.definition.properties.data.value])]); }, [JSON.stringify([transformations, currentState, component.definition.properties.data.value])]);
useEffect(() => { useEffect(() => {

View file

@ -22,6 +22,8 @@ import { shallow } from 'zustand/shallow';
import _ from 'lodash'; import _ from 'lodash';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import { diff } from 'deep-object-diff'; import { diff } from 'deep-object-diff';
import { isPDFSupported } from '@/_stores/utils';
import toast from 'react-hot-toast';
const NO_OF_GRIDS = 43; const NO_OF_GRIDS = 43;
@ -256,6 +258,13 @@ export const Container = ({
return; return;
} }
if (item.component.component === 'PDF' && !isPDFSupported()) {
toast.error(
'PDF is not supported in this version of browser. We recommend upgrading to the latest version for full support.'
);
return;
}
if (item.name === 'comment') { if (item.name === 'comment') {
const canvasBoundingRect = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect(); const canvasBoundingRect = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect();
const offsetFromTopOfWindow = canvasBoundingRect.top; const offsetFromTopOfWindow = canvasBoundingRect.top;

View file

@ -18,6 +18,7 @@ import { useMounted } from '@/_hooks/use-mount';
import { useEditorStore } from '@/_stores/editorStore'; import { useEditorStore } from '@/_stores/editorStore';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import { diff } from 'deep-object-diff'; import { diff } from 'deep-object-diff';
import { isPDFSupported } from '@/_stores/utils';
const NO_OF_GRIDS = 43; const NO_OF_GRIDS = 43;
@ -308,6 +309,13 @@ export const SubContainer = ({
() => ({ () => ({
accept: ItemTypes.BOX, accept: ItemTypes.BOX,
drop(item, monitor) { drop(item, monitor) {
if (item.component.component === 'PDF' && !isPDFSupported()) {
toast.error(
'PDF is not supported in this version of browser. We recommend upgrading to the latest version for full support.'
);
return;
}
const componentMeta = _.cloneDeep( const componentMeta = _.cloneDeep(
componentTypes.find((component) => component.component === item.component.component) componentTypes.find((component) => component.component === item.component.component)
); );

View file

@ -15,8 +15,10 @@ function DisablePasswordLoginModal({ show, disablePasswordLogin, setShowModal, r
const modalContent = ( const modalContent = (
<div> <div>
<p>Disable password login only if you have configured SSO or else you will get locked out.</p> <p data-cy="modal-message">
<p>Are you sure you want to continue?</p> Disable password login only if you have configured SSO or else you will get locked out.
</p>
<p data-cy="confrmation-text">Are you sure you want to continue?</p>
</div> </div>
); );
@ -25,7 +27,7 @@ function DisablePasswordLoginModal({ show, disablePasswordLogin, setShowModal, r
<ButtonSolid variant="tertiary" onClick={handleClose} data-cy="cancel-button"> <ButtonSolid variant="tertiary" onClick={handleClose} data-cy="cancel-button">
Cancel Cancel
</ButtonSolid> </ButtonSolid>
<ButtonSolid variant={'dangerPrimary'} onClick={handleDisable}> <ButtonSolid variant={'dangerPrimary'} onClick={handleDisable} data-cy="confirmation-button">
Disable Disable
</ButtonSolid> </ButtonSolid>
</> </>

View file

@ -134,8 +134,8 @@ export function GithubSSOModal({ settings, onClose, onUpdateSSOSettings, isInsta
}} }}
> >
<div> <div>
<label className="switch"> <label className="switch" data-cy="github-toggle-input">
<input type="checkbox" checked={enabled} onChange={onToggleChange} data-cy="github-toggle-input" /> <input type="checkbox" checked={enabled} onChange={onToggleChange} />
<span className="slider round"></span> <span className="slider round"></span>
</label> </label>
<span className="sso-type-header" data-cy="card-title" style={{ marginBottom: '0px', fontWeight: '500' }}> <span className="sso-type-header" data-cy="card-title" style={{ marginBottom: '0px', fontWeight: '500' }}>

View file

@ -104,8 +104,8 @@ export function GoogleSSOModal({ settings, onClose, onUpdateSSOSettings, isInsta
}} }}
> >
<div> <div>
<label className="switch"> <label className="switch" data-cy="google-enable-toggle">
<input type="checkbox" checked={enabled} onChange={onToggleChange} data-cy="google-enable-toggle" /> <input type="checkbox" checked={enabled} onChange={onToggleChange} />
<span className="slider round"></span> <span className="slider round"></span>
</label> </label>
<span className="sso-type-header" data-cy="card-title" style={{ marginBottom: '0px', fontWeight: '500' }}> <span className="sso-type-header" data-cy="card-title" style={{ marginBottom: '0px', fontWeight: '500' }}>

View file

@ -241,7 +241,12 @@ class SSOConfiguration extends React.Component {
const isEnabled = this.state[isEnabledKey]; const isEnabled = this.state[isEnabledKey];
return ( return (
<div className="sso-option" key={key} onClick={() => this.openModal(key)} data-cy="sso-card"> <div
className="sso-option"
key={key}
onClick={() => this.openModal(key)}
data-cy={`${name.toLowerCase().replace(/\s+/g, '-')}-sso-card`}
>
<div className="sso-option-label"> <div className="sso-option-label">
{ {
<div <div
@ -303,7 +308,7 @@ class SSOConfiguration extends React.Component {
justifyContent: 'flex-start', justifyContent: 'flex-start',
}} }}
bsPrefix="no-caret-dropdown-toggle" bsPrefix="no-caret-dropdown-toggle"
data-testid="instance-sso-toggle" data-cy="dropdown-custom-toggle"
> >
<div <div
className="sso-option-label" className="sso-option-label"
@ -346,7 +351,7 @@ class SSOConfiguration extends React.Component {
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
<label className="switch" style={{ marginLeft: '95px' }}> <label className="switch" style={{ marginLeft: '95px' }} data-cy="instance-sso-toggle">
<input type="checkbox" checked={defaultSSO} onChange={this.toggleDefaultSSO} /> <input type="checkbox" checked={defaultSSO} onChange={this.toggleDefaultSSO} />
<span className="slider round"></span> <span className="slider round"></span>
</label> </label>

View file

@ -5,7 +5,9 @@ export function handleResponse(response, avoidRedirection = false) {
const data = text && JSON.parse(text); const data = text && JSON.parse(text);
if (!response.ok) { if (!response.ok) {
if ([401].indexOf(response.status) !== -1) { if ([401].indexOf(response.status) !== -1) {
avoidRedirection ? authenticationService.logout() : location.reload(true); const errorMessageJson = typeof data.message === 'string' ? JSON.parse(data.message) : undefined;
const workspaceId = errorMessageJson?.organizationId;
avoidRedirection ? authenticationService.logout(false, workspaceId) : location.reload(true);
} }
const error = (data && data.message) || response.statusText; const error = (data && data.message) || response.statusText;

View file

@ -294,16 +294,16 @@ function resetPassword(params) {
return fetch(`${config.apiUrl}/reset-password`, requestOptions).then(handleResponse); return fetch(`${config.apiUrl}/reset-password`, requestOptions).then(handleResponse);
} }
function logout(avoidRedirection = false) { function logout(avoidRedirection = false, organizationId = null) {
const requestOptions = { const requestOptions = {
method: 'GET', method: 'GET',
headers: authHeader(), headers: authHeader(),
credentials: 'include', credentials: 'include',
}; };
const workspaceId = getWorkspaceId() || organizationId;
const redirectToLoginPage = () => { const redirectToLoginPage = () => {
const loginPath = const loginPath = (window.public_config?.SUB_PATH || '/') + 'login' + `${workspaceId ? `/${workspaceId}` : ''}`;
(window.public_config?.SUB_PATH || '/') + 'login' + `${getWorkspaceId() ? `/${getWorkspaceId()}` : ''}`;
if (avoidRedirection) { if (avoidRedirection) {
window.location.href = loginPath; window.location.href = loginPath;
} else { } else {

View file

@ -102,7 +102,7 @@ function getUsersNotInGroup(searchInput, groupPermissionId) {
credentials: 'include', credentials: 'include',
}; };
return fetch( return fetch(
`${config.apiUrl}/group_permissions/${groupPermissionId}/addable_users?input=${searchInput}`, `${config.apiUrl}/group_permissions/${groupPermissionId}/addable_users?input=${searchInput.trim()}`,
requestOptions requestOptions
).then(handleResponse); ).then(handleResponse);
} }

View file

@ -329,3 +329,68 @@ function toRemoveExposedvariablesFromComponentDiff(object) {
return copy; return copy;
} }
export function isPDFSupported() {
const browser = getBrowserUserAgent();
if (!browser) {
return true;
}
const isChrome = browser.name === 'Chrome' && browser.major >= 92;
const isEdge = browser.name === 'Edge' && browser.major >= 92;
const isSafari = browser.name === 'Safari' && browser.major >= 15 && browser.minor >= 4; // Handle minor version check for Safari
const isFirefox = browser.name === 'Firefox' && browser.major >= 90;
console.log('browser--', browser, isChrome || isEdge || isSafari || isFirefox);
return isChrome || isEdge || isSafari || isFirefox;
}
export function getBrowserUserAgent(userAgent) {
var regexps = {
Chrome: [/Chrome\/(\S+)/],
Firefox: [/Firefox\/(\S+)/],
MSIE: [/MSIE (\S+);/],
Opera: [/Opera\/.*?Version\/(\S+)/ /* Opera 10 */, /Opera\/(\S+)/ /* Opera 9 and older */],
Safari: [/Version\/(\S+).*?Safari\//],
},
re,
m,
browser,
version;
if (userAgent === undefined) userAgent = navigator.userAgent;
for (browser in regexps)
while ((re = regexps[browser].shift()))
if ((m = userAgent.match(re))) {
version = m[1].match(new RegExp('[^.]+(?:.[^.]+){0,1}'))[0];
const { major, minor } = extractVersion(version);
return {
name: browser,
major,
minor,
};
}
return null;
}
function extractVersion(versionStr) {
// Split the string by "."
const parts = versionStr.split('.');
// Check for valid input
if (parts.length === 0 || parts.some((part) => isNaN(part))) {
return { major: null, minor: null };
}
// Extract major version
const major = parseInt(parts[0], 10);
// Handle minor version (default to 0)
const minor = parts.length > 1 ? parseInt(parts[1], 10) : 0;
return { major, minor };
}

View file

@ -1 +1 @@
2.32.3 2.32.5

View file

@ -110,8 +110,8 @@ export class GroupPermissionsController {
@UseGuards(JwtAuthGuard, PoliciesGuard) @UseGuards(JwtAuthGuard, PoliciesGuard)
@CheckPolicies((ability: AppAbility) => ability.can('accessGroupPermission', UserEntity)) @CheckPolicies((ability: AppAbility) => ability.can('accessGroupPermission', UserEntity))
@Get(':id/addable_users') @Get(':id/addable_users')
async addableUsers(@User() user, @Param('id') id, @Query('input') searchInput) { async addableUsers(@User() user, @Param('id') id, @Query('input') searchInput: string) {
const users = await this.groupPermissionsService.findAddableUsers(user, id, searchInput); const users = await this.groupPermissionsService.findAddableUsers(user, id, searchInput.trim());
return decamelizeKeys({ users }); return decamelizeKeys({ users });
} }

View file

@ -1,4 +1,4 @@
import { ExecutionContext, Injectable, NotFoundException } from '@nestjs/common'; import { ExecutionContext, Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { AppsService } from 'src/services/apps.service'; import { AppsService } from 'src/services/apps.service';
@ -25,6 +25,17 @@ export class AppAuthGuard extends AuthGuard('jwt') {
return true; return true;
} }
return super.canActivate(context); // Throw a custom exception with workspace ID if the app is not public
try {
const authResult = await super.canActivate(context);
return authResult;
} catch (error) {
throw new UnauthorizedException(
JSON.stringify({
organizationId: app?.organizationId,
message: 'Authentication is required to access this app.',
})
);
}
} }
} }

View file

@ -470,15 +470,17 @@ export class GroupPermissionsService {
const getOrConditions = () => { const getOrConditions = () => {
return new Brackets((qb) => { return new Brackets((qb) => {
qb.orWhere('lower(user.email) like :email', { if (searchInput) {
email: `%${searchInput.toLowerCase()}%`, qb.orWhere('lower(user.email) like :email', {
}); email: `%${searchInput.toLowerCase()}%`,
qb.orWhere('lower(user.firstName) like :firstName', { });
firstName: `%${searchInput.toLowerCase()}%`, qb.orWhere('lower(user.firstName) like :firstName', {
}); firstName: `%${searchInput.toLowerCase()}%`,
qb.orWhere('lower(user.lastName) like :lastName', { });
lastName: `%${searchInput.toLowerCase()}%`, qb.orWhere('lower(user.lastName) like :lastName', {
}); lastName: `%${searchInput.toLowerCase()}%`,
});
}
}); });
}; };
@ -490,14 +492,13 @@ export class GroupPermissionsService {
'organization_users.organizationId = :organizationId', 'organization_users.organizationId = :organizationId',
{ organizationId: user.organizationId } { organizationId: user.organizationId }
) )
.andWhere(getOrConditions) .where('user.id NOT IN (:...userList)', { userList: [...usersInGroupIds, ...adminUserIds] })
.where('user.id NOT IN (:...userList)', { userList: [...usersInGroupIds, ...adminUserIds] }); .andWhere(getOrConditions());
if (searchInput) { if (!searchInput) {
builtQuery.andWhere(getOrConditions);
} else {
builtQuery.take(10); // Limiting to 10 users if there's no search input builtQuery.take(10); // Limiting to 10 users if there's no search input
} }
builtQuery.orderBy('user.firstName'); builtQuery.orderBy('user.firstName');
return await builtQuery.getMany(); return await builtQuery.getMany();
} }