diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/saml/okta_login_spec.js b/e2e-tests/cypress/tests/integration/channels/enterprise/saml/okta_login_spec.ts
similarity index 99%
rename from e2e-tests/cypress/tests/integration/channels/enterprise/saml/okta_login_spec.js
rename to e2e-tests/cypress/tests/integration/channels/enterprise/saml/okta_login_spec.ts
index c37fabb6e7..a409b34a15 100644
--- a/e2e-tests/cypress/tests/integration/channels/enterprise/saml/okta_login_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/enterprise/saml/okta_login_spec.ts
@@ -11,6 +11,7 @@
// Group: @channels @enterprise @saml
// Skip: @headless @electron @firefox // run on Chrome (headed) only
+import {UserCollection} from 'tests/support/okta_commands';
import users from '../../../../fixtures/saml_users.json';
//Manual Setup required: Follow the instructions mentioned in the mattermost/platform-private/config/saml-okta-setup.txt file
@@ -92,7 +93,7 @@ context('Okta', () => {
// # Check SAML metadata if working properly
cy.apiGetMetadataFromIdp(idpMetadataUrl);
- cy.oktaAddUsers(users);
+ cy.oktaAddUsers(users as unknown as UserCollection);
cy.apiUpdateConfig(newConfig).then(({config}) => {
cy.setTestSettings(loginButtonText, config).then((_response) => {
testSettings = _response;
diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_automated_spec.js b/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_automated_spec.ts
similarity index 98%
rename from e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_automated_spec.js
rename to e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_automated_spec.ts
index 70d8950c5e..ebac0b3f21 100644
--- a/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_automated_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_automated_spec.ts
@@ -11,6 +11,7 @@
// Group: @channels @enterprise @saml
// Skip: @headless @electron @firefox // run on Chrome (headed) only
+import {UserCollection} from 'tests/support/okta_commands';
import users from '../../../../fixtures/saml_users.json';
//Manual Setup required: Follow the instructions mentioned in the mattermost/platform-private/config/saml-okta-setup.txt file
@@ -88,7 +89,7 @@ context('LDAP SAML - Automated Tests (SAML TESTS)', () => {
// # Check SAML metadata if working properly
cy.apiGetMetadataFromIdp(idpMetadataUrl);
- cy.oktaAddUsers(users);
+ cy.oktaAddUsers(users as unknown as UserCollection);
cy.apiUpdateConfig(newConfig).then(({config}) => {
cy.setTestSettings(loginButtonText, config).then((_response) => {
testSettings = _response;
diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_guest_member_spec.js b/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_guest_member_spec.ts
similarity index 98%
rename from e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_guest_member_spec.js
rename to e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_guest_member_spec.ts
index 05aaf967c1..c4b5b1e0b1 100644
--- a/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_guest_member_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_guest_member_spec.ts
@@ -82,7 +82,7 @@ describe('SAML Guest', () => {
cy.apiUpdateConfig(newConfig).then(({config}) => {
cy.setTestSettings(loginButtonText, config).then((_response) => {
testSettings = _response;
- cy.keycloakResetUsers({guestUser});
+ cy.keycloakResetUsers([guestUser]);
});
});
});
@@ -147,7 +147,7 @@ describe('SAML Guest', () => {
const testConfig = {
...newConfig,
GuestAccountsSettings: {
- ...newConfig.GuestAccountSettings,
+ ...newConfig.GuestAccountsSettings,
Enable: true,
},
SamlSettings: {
diff --git a/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_metadata_spec.js b/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_metadata_spec.ts
similarity index 97%
rename from e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_metadata_spec.js
rename to e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_metadata_spec.ts
index e0053d5309..324a21e359 100644
--- a/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_metadata_spec.js
+++ b/e2e-tests/cypress/tests/integration/channels/enterprise/saml/saml_metadata_spec.ts
@@ -1,6 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
+import {AdminConfig} from '@mattermost/types/config';
+
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
@@ -18,7 +20,7 @@ const testIdpURL = 'http://test_idp_url';
const testIdpDescriptorURL = 'http://test_idp_descriptor_url';
const getSamlMetadataErrorMessage = 'SAML Metadata URL did not connect and pull data successfully';
-let config;
+let config: AdminConfig;
describe('SystemConsole->SAML 2.0 - Get Metadata from Idp Flow', () => {
before(() => {
diff --git a/e2e-tests/cypress/tests/support/keycloak_commands.d.ts b/e2e-tests/cypress/tests/support/keycloak_commands.d.ts
deleted file mode 100644
index 34c485ba08..0000000000
--- a/e2e-tests/cypress/tests/support/keycloak_commands.d.ts
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-///
-
-// ***************************************************************
-// Each command should be properly documented using JSDoc.
-// See https://jsdoc.app/index.html for reference.
-// Basic requirements for documentation are the following:
-// - Meaningful description
-// - Each parameter with `@params`
-// - Return value with `@returns`
-// - Example usage with `@example`
-// Custom command should follow naming convention of having `keycloak` prefix, e.g. `keycloakActivateUser`.
-// ***************************************************************
-
-declare namespace Cypress {
- interface Chainable {
-
- /**
- * keycloakGetAccessTokenAPI is a task wrapped as command with post-verification
- * that an Access Token is successfully retrieved
- * @returns {string} - access token
- */
- keycloakGetAccessTokenAPI(): Chainable;
-
- /**
- * keycloakCreateUserAPI is a task wrapped as command with post-verification
- * that a user is successfully created in keycloak
- * @param {string} accessToken - a valid access token
- * @param {object} user - a keycloak user object to create
- *
- * @example
- * cy.keycloakCreateUserAPI('abcde', {firstName: 'test', lastName: 'test', email: 'test', username: 'test', enabled: true,});
- */
- keycloakCreateUserAPI(accessToken: string, user: any): Chainable;
-
- /**
- * keycloakResetPasswordAPI is a task wrapped as command with post-verification
- * that a user password is successfully reset in keycloak
- * @param {string} accessToken - a valid access token
- * @param {string} userId - a keycloak userId
- * @param {string} password - new password to set
- *
- * @example
- * cy.keycloakResetPasswordAPI('abcde', '12345', 'password');
- */
- keycloakResetPasswordAPI(accessToken: string, userId: string, password: string): Chainable;
-
- /**
- * keycloakGetUserAPI is a task wrapped as command with post-verification
- * that a user is successfully found in keycloak
- * @param {string} accessToken - a valid access token
- * @param {string} email - an email to query
- * @returns {string} - keycloak userId if found
- *
- * @example
- * cy.keycloakGetUserAPI('abcde', 'test@mm.com');
- */
- keycloakGetUserAPI(accessToken: string, email: string): Chainable;
-
- /**
- * keycloakDeleteUserAPI is a task wrapped as command with post-verification
- * that a user is successfully deleted in keycloak
- * @param {string} accessToken - a valid access token
- * @param {string} userId - keycloak user id to delete
- *
- * @example
- * cy.keycloakDeleteUserAPI('abcde', '12345');
- */
- keycloakDeleteUserAPI(accessToken: string, userId: string): Chainable;
-
- /**
- * keycloakUpdateUserAPI is a task wrapped as command with post-verification
- * that a user is successfully updated in keycloak
- * @param {string} accessToken - a valid access token
- * @param {string} userId - keycloak user id to delete
- * @param {object} data - keycloak user object
- *
- * @example
- * cy.keycloakUpdateUserAPI('abcde', '12345', {'enabled': false}});
- */
- keycloakUpdateUserAPI(accessToken: string, userId: string, data: any): Chainable;
-
- /**
- * keycloakDeleteSessionAPI is a task wrapped as command with post-verification
- * that a users session is successfully deleted in keycloak
- * @param {string} accessToken - a valid access token
- * @param {string} sessionId- keycloak session id to delete
- *
- * @example
- * cy.keycloakDeleteSessionAPI('abcde', '12345');
- */
- keycloakDeleteSessionAPI(accessToken: string, sessionId: string): Chainable;
-
- /**
- * keycloakGetUserSessionsAPI is a task wrapped as command with post-verification
- * that a users sessions are successfully found
- * @param {string} accessToken - a valid access token
- * @param {string} userId - keycloak user id to find sessions
- * @returns {string[]} - array of keycloak session ids
- *
- * @example
- * cy.keycloakGetUserSessionsAPI('abcde', '12345');
- */
- keycloakGetUserSessionsAPI(accessToken: string, userId: string): Chainable ;
-
- /**
- * keycloakDeleteUserSessions is a command that finds a user's sessions
- * and deletes them.
- * @param {string} accessToken - a valid access token
- * @param {string} userId- keycloak user id to delete sessions
- *
- * @example
- * cy.keycloakDeleteUserSessions('abcde', '12345');
- */
- keycloakDeleteUserSessions(accessToken: string, userId: string): Chainable;
-
- /**
- * keycloakResetUsers is a command that "resets" (deletes and re-creates) the users.
- * @param {object[]} users - an array of user objects
- *
- * @example
- * cy.keycloakResetUsers([{firstName: 'test', lastName: 'test', email: 'test', username: 'test', enabled: true}]);
- */
- keycloakResetUsers(users: any[]): Chainable;
-
- /**
- * keycloakCreateUser is a command that creates a keycloak user.
- * @param {User} user - a user object
- *
- * @example
- * cy.keycloakCreateUser({firstName: 'test', lastName: 'test', email: 'test', username: 'test', enabled: true});
- */
- keycloakCreateUser(user: any): Chainable;
-
- /**
- * keycloakSuspendUser is a command that suspends a user (enabled=false)
- * @param {string} userEmail - email of keycloak user
- *
- * @example
- * cy.keycloakSuspendUser('user@test.com');
- */
- keycloakSuspendUser(userEmail: string): Chainable;
-
- /**
- * keycloakUnsuspendUser is a command that re-activates a user (enabled=true)
- * @param {string} userEmail - email of keycloak user
- *
- * @example
- * cy.keycloakUnsuspendUser('user@test.com');
- */
- keycloakUnsuspendUser(userEmail: string): Chainable;
-
- /**
- * checkKeycloakLoginPage is a command that verifies the keycloak login page is displayed
- *
- * @example
- * cy.checkKeycloakLoginPage();
- */
- checkKeycloakLoginPage(): Chainable;
-
- /**
- * doKeycloakLogin is a command that attempts to log a user into keycloak.
- *
- * @example
- * cy.doKeycloakLogin();
- */
- doKeycloakLogin(user): Chainable;
-
- /**
- * verifyKeycloakLoginFailed is a command that verifies a keycloak login failed.
- *
- * @example
- * cy.verifyKeycloakLoginFailed();
- */
- verifyKeycloakLoginFailed(): Chainable;
- }
-}
diff --git a/e2e-tests/cypress/tests/support/keycloak_commands.js b/e2e-tests/cypress/tests/support/keycloak_commands.js
deleted file mode 100644
index 41beba58ef..0000000000
--- a/e2e-tests/cypress/tests/support/keycloak_commands.js
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-import * as TIMEOUTS from '../fixtures/timeouts';
-
-const {
- keycloakBaseUrl,
- keycloakAppName,
-} = Cypress.env();
-
-const baseUrl = `${keycloakBaseUrl}/auth/admin/realms/${keycloakAppName}`;
-const loginUrl = `${keycloakBaseUrl}/auth/realms/master/protocol/openid-connect/token`;
-
-function buildProfile(user) {
- return {
- firstName: user.firstname,
- lastName: user.lastname,
- email: user.email,
- username: user.username,
- enabled: true,
- };
-}
-
-Cypress.Commands.add('keycloakGetAccessTokenAPI', () => {
- return cy.task('keycloakRequest', {
- baseUrl: loginUrl,
- path: '',
- method: 'post',
- headers: {'Content-type': 'application/x-www-form-urlencoded'},
- data: 'grant_type=password&username=mmuser&password=mostest&client_id=admin-cli',
- }).then((response) => {
- expect(response.status).to.equal(200);
- const token = response.data.access_token;
- return cy.wrap(token);
- });
-});
-
-Cypress.Commands.add('keycloakCreateUserAPI', (accessToken, user = {}) => {
- const profile = buildProfile(user);
- return cy.task('keycloakRequest', {
- baseUrl,
- path: 'users',
- method: 'post',
- data: profile,
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${accessToken}`,
- },
- }).then((response) => {
- expect(response.status).to.equal(201);
- });
-});
-
-Cypress.Commands.add('keycloakResetPasswordAPI', (accessToken, userId, password) => {
- return cy.task('keycloakRequest', {
- baseUrl,
- path: `users/${userId}/reset-password`,
- method: 'put',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${accessToken}`,
- },
- data: {type: 'password', temporary: false, value: password},
- }).then((response) => {
- if (response.status === 200 && response.data.length > 0) {
- return cy.wrap(response.data[0].id);
- }
- return null;
- });
-});
-
-Cypress.Commands.add('keycloakGetUserAPI', (accessToken, email) => {
- return cy.task('keycloakRequest', {
- baseUrl,
- path: 'users?email=' + email,
- method: 'get',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${accessToken}`,
- },
- }).then((response) => {
- if (response.status === 200 && response.data.length > 0) {
- return cy.wrap(response.data[0].id);
- }
- return null;
- });
-});
-
-Cypress.Commands.add('keycloakDeleteUserAPI', (accessToken, userId) => {
- return cy.task('keycloakRequest', {
- baseUrl,
- path: `users/${userId}`,
- method: 'delete',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${accessToken}`,
- },
- }).then((response) => {
- expect(response.status).to.equal(204);
- expect(response.data).is.empty;
- });
-});
-
-Cypress.Commands.add('keycloakUpdateUserAPI', (accessToken, userId, data) => {
- return cy.task('keycloakRequest', {
- baseUrl,
- path: 'users/' + userId,
- method: 'put',
- headers: {
- Authorization: `Bearer ${accessToken}`,
- },
- data,
- }).then((response) => {
- expect(response.status).to.equal(204);
- expect(response.data).is.empty;
- });
-});
-
-Cypress.Commands.add('keycloakDeleteSessionAPI', (accessToken, sessionId) => {
- return cy.task('keycloakRequest', {
- baseUrl,
- path: `sessions/${sessionId}`,
- method: 'delete',
- headers: {
- Authorization: `Bearer ${accessToken}`,
- },
- }).then((delResponse) => {
- expect(delResponse.status).to.equal(204);
- expect(delResponse.data).is.empty;
- });
-});
-
-Cypress.Commands.add('keycloakGetUserSessionsAPI', (accessToken, userId) => {
- return cy.task('keycloakRequest', {
- baseUrl,
- path: `users/${userId}/sessions`,
- method: 'get',
- headers: {
- 'Content-Type': 'application/json',
- Authorization: `Bearer ${accessToken}`,
- },
- }).then((response) => {
- expect(response.status).to.equal(200);
- expect(response.data);
- return cy.wrap(response.data);
- });
-});
-
-Cypress.Commands.add('keycloakDeleteUserSessions', (accessToken, userId) => {
- return cy.keycloakGetUserSessionsAPI(accessToken, userId).then((responseData) => {
- if (responseData.length > 0) {
- Object.values(responseData).forEach((data) => {
- const sessionId = data.id;
- cy.keycloakDeleteSession(accessToken, sessionId);
- });
-
- // Ensure we clear out these specific cookies
- ['JSESSIONID'].forEach((cookie) => {
- cy.clearCookie(cookie);
- });
- }
- });
-});
-
-Cypress.Commands.add('keycloakResetUsers', (users) => {
- return cy.keycloakGetAccessTokenAPI().then((accessToken) => {
- Object.values(users).forEach((_user) => {
- cy.keycloakGetUserAPI(accessToken, _user.email).then((userId) => {
- if (userId) {
- cy.keycloakDeleteUserAPI(accessToken, userId);
- }
- }).then(() => {
- cy.keycloakCreateUser(accessToken, _user).then((_id) => {
- _user.keycloakId = _id;
- });
- });
- });
- });
-});
-
-Cypress.Commands.add('keycloakCreateUser', (accessToken, user) => {
- return cy.keycloakCreateUserAPI(accessToken, user).then(() => {
- cy.keycloakGetUserAPI(accessToken, user.email).then((newId) => {
- cy.keycloakResetPasswordAPI(accessToken, newId, user.password).then(() => {
- cy.keycloakDeleteUserSessions(accessToken, newId).then(() => {
- return cy.wrap(newId);
- });
- });
- });
- });
-});
-
-Cypress.Commands.add('keycloakCreateUsers', (users = []) => {
- return cy.keycloakGetAccessTokenAPI().then((accessToken) => {
- return users.forEach((user) => {
- return cy.keycloakCreateUser(accessToken, user);
- });
- });
-});
-
-Cypress.Commands.add('keycloakUpdateUser', (userEmail, data) => {
- return cy.keycloakGetAccessTokenAPI().then((accessToken) => {
- return cy.keycloakGetUserAPI(accessToken, userEmail).then((userId) => {
- return cy.keycloakUpdateUserAPI(accessToken, userId, data);
- });
- });
-});
-
-Cypress.Commands.add('keycloakSuspendUser', (userEmail) => {
- const data = {enabled: false};
- cy.keycloakUpdateUser(userEmail, data);
-});
-
-Cypress.Commands.add('keycloakUnsuspendUser', (userEmail) => {
- const data = {enabled: true};
- cy.keycloakUpdateUser(userEmail, data);
-});
-
-Cypress.Commands.add('checkKeycloakLoginPage', () => {
- cy.findByText('Username or email', {timeout: TIMEOUTS.ONE_SEC}).should('be.visible');
- cy.findByText('Password').should('be.visible');
- cy.findAllByText('Log In').should('be.visible');
-});
-
-Cypress.Commands.add('doKeycloakLogin', (user) => {
- cy.apiLogout();
- cy.visit('/login');
- cy.findByText('SAML').click();
- cy.findByText('Username or email').type(user.email);
- cy.findByText('Password').type(user.password);
- cy.findAllByText('Log In').last().click();
-});
-
-Cypress.Commands.add('verifyKeycloakLoginFailed', () => {
- cy.findAllByText('Account is disabled, contact your administrator.').should('be.visible');
-});
diff --git a/e2e-tests/cypress/tests/support/keycloak_commands.ts b/e2e-tests/cypress/tests/support/keycloak_commands.ts
new file mode 100644
index 0000000000..0b896cc9d3
--- /dev/null
+++ b/e2e-tests/cypress/tests/support/keycloak_commands.ts
@@ -0,0 +1,425 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {ChainableT} from 'tests/types';
+import * as TIMEOUTS from '../fixtures/timeouts';
+
+const {
+ keycloakBaseUrl,
+ keycloakAppName,
+} = Cypress.env();
+
+const baseUrl = `${keycloakBaseUrl}/auth/admin/realms/${keycloakAppName}`;
+const loginUrl = `${keycloakBaseUrl}/auth/realms/master/protocol/openid-connect/token`;
+
+function buildProfile(user) {
+ return {
+ firstName: user.firstname,
+ lastName: user.lastname,
+ email: user.email,
+ username: user.username,
+ enabled: true,
+ };
+}
+
+/**
+* keycloakGetAccessTokenAPI is a task wrapped as command with post-verification
+* that an Access Token is successfully retrieved
+* @returns {string} - access token
+*/
+function keycloakGetAccessTokenAPI(): ChainableT {
+ return cy.task('keycloakRequest', {
+ baseUrl: loginUrl,
+ path: '',
+ method: 'post',
+ headers: {'Content-type': 'application/x-www-form-urlencoded'},
+ data: 'grant_type=password&username=mmuser&password=mostest&client_id=admin-cli',
+ }).then((response: any) => {
+ expect(response.status).to.equal(200);
+ const token = response.data.access_token;
+ return cy.wrap(token as string);
+ });
+}
+
+Cypress.Commands.add('keycloakGetAccessTokenAPI', keycloakGetAccessTokenAPI);
+
+/**
+* keycloakCreateUserAPI is a task wrapped as command with post-verification
+* that a user is successfully created in keycloak
+* @param {string} accessToken - a valid access token
+* @param {object} user - a keycloak user object to create
+*
+* @example
+* cy.keycloakCreateUserAPI('abcde', {firstName: 'test', lastName: 'test', email: 'test', username: 'test', enabled: true,});
+*/
+function keycloakCreateUserAPI(accessToken: string, user: any = {}) {
+ const profile = buildProfile(user);
+ return cy.task('keycloakRequest', {
+ baseUrl,
+ path: 'users',
+ method: 'post',
+ data: profile,
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ }).then((response: Response) => {
+ expect(response.status).to.equal(201);
+ });
+}
+
+Cypress.Commands.add('keycloakCreateUserAPI', keycloakCreateUserAPI);
+
+/**
+* keycloakResetPasswordAPI is a task wrapped as command with post-verification
+* that a user password is successfully reset in keycloak
+* @param {string} accessToken - a valid access token
+* @param {string} userId - a keycloak userId
+* @param {string} password - new password to set
+*
+* @example
+* cy.keycloakResetPasswordAPI('abcde', '12345', 'password');
+*/
+function keycloakResetPasswordAPI(accessToken: string, userId: string, password: string): ChainableT {
+ return cy.task('keycloakRequest', {
+ baseUrl,
+ path: `users/${userId}/reset-password`,
+ method: 'put',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ data: {type: 'password', temporary: false, value: password},
+ }).then((response: any) => {
+ if (response.status === 200 && response.data.length > 0) {
+ return cy.wrap(response.data[0].id);
+ }
+ return null;
+ });
+}
+
+Cypress.Commands.add('keycloakResetPasswordAPI', keycloakResetPasswordAPI);
+
+/**
+* keycloakGetUserAPI is a task wrapped as command with post-verification
+* that a user is successfully found in keycloak
+* @param {string} accessToken - a valid access token
+* @param {string} email - an email to query
+* @returns {string} - keycloak userId if found
+*
+* @example
+* cy.keycloakGetUserAPI('abcde', 'test@mm.com');
+*/
+function keycloakGetUserAPI(accessToken: string, email: string): ChainableT {
+ return cy.task('keycloakRequest', {
+ baseUrl,
+ path: 'users?email=' + email,
+ method: 'get',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ }).then((response: any) => {
+ if (response.status === 200 && response.data.length > 0) {
+ return cy.wrap(response.data[0].id);
+ }
+ return null;
+ });
+}
+
+Cypress.Commands.add('keycloakGetUserAPI', keycloakGetUserAPI);
+
+/**
+* keycloakDeleteUserAPI is a task wrapped as command with post-verification
+* that a user is successfully deleted in keycloak
+* @param {string} accessToken - a valid access token
+* @param {string} userId - keycloak user id to delete
+*
+* @example
+* cy.keycloakDeleteUserAPI('abcde', '12345');
+*/
+function keycloakDeleteUserAPI(accessToken: string, userId: string): ChainableT {
+ return cy.task('keycloakRequest', {
+ baseUrl,
+ path: `users/${userId}`,
+ method: 'delete',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ }).then((response: any) => {
+ expect(response.status).to.equal(204);
+ expect(response.data).is.empty;
+ });
+}
+
+Cypress.Commands.add('keycloakDeleteUserAPI', keycloakDeleteUserAPI);
+
+/**
+* keycloakUpdateUserAPI is a task wrapped as command with post-verification
+* that a user is successfully updated in keycloak
+* @param {string} accessToken - a valid access token
+* @param {string} userId - keycloak user id to delete
+* @param {object} data - keycloak user object
+*
+* @example
+* cy.keycloakUpdateUserAPI('abcde', '12345', {'enabled': false}});
+*/
+function keycloakUpdateUserAPI(accessToken: string, userId: string, data: any): ChainableT {
+ return cy.task('keycloakRequest', {
+ baseUrl,
+ path: 'users/' + userId,
+ method: 'put',
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ },
+ data,
+ }).then((response: any) => {
+ expect(response.status).to.equal(204);
+ expect(response.data).is.empty;
+ });
+}
+
+Cypress.Commands.add('keycloakUpdateUserAPI', keycloakUpdateUserAPI);
+
+/**
+* keycloakDeleteSessionAPI is a task wrapped as command with post-verification
+* that a users session is successfully deleted in keycloak
+* @param {string} accessToken - a valid access token
+* @param {string} sessionId- keycloak session id to delete
+*
+* @example
+* cy.keycloakDeleteSessionAPI('abcde', '12345');
+*/
+function keycloakDeleteSessionAPI(accessToken: string, sessionId: string): ChainableT {
+ return cy.task('keycloakRequest', {
+ baseUrl,
+ path: `sessions/${sessionId}`,
+ method: 'delete',
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ },
+ }).then((delResponse: any) => {
+ expect(delResponse.status).to.equal(204);
+ expect(delResponse.data).is.empty;
+ });
+}
+
+Cypress.Commands.add('keycloakDeleteSessionAPI', keycloakDeleteSessionAPI);
+
+/**
+* keycloakGetUserSessionsAPI is a task wrapped as command with post-verification
+* that a users sessions are successfully found
+* @param {string} accessToken - a valid access token
+* @param {string} userId - keycloak user id to find sessions
+* @returns {string[]} - array of keycloak session ids
+*
+* @example
+* cy.keycloakGetUserSessionsAPI('abcde', '12345');
+*/
+function keycloakGetUserSessionsAPI(accessToken: string, userId: string): ChainableT {
+ return cy.task('keycloakRequest', {
+ baseUrl,
+ path: `users/${userId}/sessions`,
+ method: 'get',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ },
+ }).then((response: any) => {
+ expect(response.status).to.equal(200);
+ expect(response.data);
+ return cy.wrap(response.data);
+ });
+}
+
+Cypress.Commands.add('keycloakGetUserSessionsAPI', keycloakGetUserSessionsAPI);
+
+/**
+* keycloakDeleteUserSessions is a command that finds a user's sessions
+* and deletes them.
+* @param {string} accessToken - a valid access token
+* @param {string} userId- keycloak user id to delete sessions
+*
+* @example
+* cy.keycloakDeleteUserSessions('abcde', '12345');
+*/
+function keycloakDeleteUserSessions(accessToken: string, userId: string): ChainableT {
+ return cy.keycloakGetUserSessionsAPI(accessToken, userId).then((responseData) => {
+ if (responseData.length > 0) {
+ responseData.forEach((sessionId) => {
+ cy.keycloakDeleteSessionAPI(accessToken, sessionId);
+ });
+
+ // Ensure we clear out these specific cookies
+ ['JSESSIONID'].forEach((cookie) => {
+ cy.clearCookie(cookie);
+ });
+ }
+ });
+}
+
+Cypress.Commands.add('keycloakDeleteUserSessions', keycloakDeleteUserSessions);
+
+/**
+* keycloakResetUsers is a command that "resets" (deletes and re-creates) the users.
+* @param {object[]} users - an array of user objects
+*
+* @example
+* cy.keycloakResetUsers([{firstName: 'test', lastName: 'test', email: 'test', username: 'test', enabled: true}]);
+*/
+function keycloakResetUsers(users: any[]): ChainableT {
+ return cy.keycloakGetAccessTokenAPI().then((accessToken) => {
+ users.forEach((_user) => {
+ cy.keycloakGetUserAPI(accessToken, _user.email).then((userId) => {
+ if (userId) {
+ cy.keycloakDeleteUserAPI(accessToken, userId);
+ }
+ }).then(() => {
+ cy.keycloakCreateUser(accessToken, _user).then((_id) => {
+ _user.keycloakId = _id;
+ });
+ });
+ });
+ });
+}
+
+Cypress.Commands.add('keycloakResetUsers', keycloakResetUsers);
+
+/**
+* keycloakCreateUser is a command that creates a keycloak user.
+* @param {User} user - a user object
+*
+* @example
+* cy.keycloakCreateUser({firstName: 'test', lastName: 'test', email: 'test', username: 'test', enabled: true});
+*/
+function keycloakCreateUser(accessToken, user: any): ChainableT {
+ return cy.keycloakCreateUserAPI(accessToken, user).then(() => {
+ cy.keycloakGetUserAPI(accessToken, user.email).then((newId) => {
+ cy.keycloakResetPasswordAPI(accessToken, newId, user.password).then(() => {
+ cy.keycloakDeleteUserSessions(accessToken, newId).then(() => {
+ return cy.wrap(newId);
+ });
+ });
+ });
+ });
+}
+
+Cypress.Commands.add('keycloakCreateUser', keycloakCreateUser);
+
+function keycloakCreateUsers(users = []) {
+ return cy.keycloakGetAccessTokenAPI().then((accessToken) => {
+ return users.forEach((user) => {
+ return cy.keycloakCreateUser(accessToken, user);
+ });
+ });
+}
+
+Cypress.Commands.add('keycloakCreateUsers', keycloakCreateUsers);
+
+function keycloakUpdateUser(userEmail, data): ChainableT {
+ return cy.keycloakGetAccessTokenAPI().then((accessToken) => {
+ return cy.keycloakGetUserAPI(accessToken, userEmail).then((userId) => {
+ return cy.keycloakUpdateUserAPI(accessToken, userId, data);
+ });
+ });
+}
+
+Cypress.Commands.add('keycloakUpdateUser', keycloakUpdateUser);
+
+/**
+* keycloakSuspendUser is a command that suspends a user (enabled=false)
+* @param {string} userEmail - email of keycloak user
+*
+* @example
+* cy.keycloakSuspendUser('user@test.com');
+*/
+function keycloakSuspendUser(userEmail: string) {
+ const data = {enabled: false};
+ cy.keycloakUpdateUser(userEmail, data);
+}
+
+Cypress.Commands.add('keycloakSuspendUser', keycloakSuspendUser);
+
+/**
+* keycloakUnsuspendUser is a command that re-activates a user (enabled=true)
+* @param {string} userEmail - email of keycloak user
+*
+* @example
+* cy.keycloakUnsuspendUser('user@test.com');
+*/
+function keycloakUnsuspendUser(userEmail: string): ChainableT {
+ const data = {enabled: true};
+ return cy.keycloakUpdateUser(userEmail, data);
+}
+
+Cypress.Commands.add('keycloakUnsuspendUser', keycloakUnsuspendUser);
+
+/**
+* checkKeycloakLoginPage is a command that verifies the keycloak login page is displayed
+*
+* @example
+* cy.checkKeycloakLoginPage();
+*/
+function checkKeycloakLoginPage() {
+ cy.findByText('Username or email', {timeout: TIMEOUTS.ONE_SEC}).should('be.visible');
+ cy.findByText('Password').should('be.visible');
+ cy.findAllByText('Log In').should('be.visible');
+}
+
+Cypress.Commands.add('checkKeycloakLoginPage', checkKeycloakLoginPage);
+
+/**
+* doKeycloakLogin is a command that attempts to log a user into keycloak.
+*
+* @example
+* cy.doKeycloakLogin();
+*/
+function doKeycloakLogin(user) {
+ cy.apiLogout();
+ cy.visit('/login');
+ cy.findByText('SAML').click();
+ cy.findByText('Username or email').type(user.email);
+ cy.findByText('Password').type(user.password);
+ cy.findAllByText('Log In').last().click();
+}
+
+Cypress.Commands.add('doKeycloakLogin', doKeycloakLogin);
+
+/**
+* verifyKeycloakLoginFailed is a command that verifies a keycloak login failed.
+*
+* @example
+* cy.verifyKeycloakLoginFailed();
+*/
+function verifyKeycloakLoginFailed() {
+ cy.findAllByText('Account is disabled, contact your administrator.').should('be.visible');
+}
+
+Cypress.Commands.add('verifyKeycloakLoginFailed', verifyKeycloakLoginFailed);
+
+declare global {
+ // eslint-disable-next-line @typescript-eslint/no-namespace
+ namespace Cypress {
+ interface Chainable {
+ keycloakCreateUsers: typeof keycloakCreateUsers;
+ keycloakUpdateUser: typeof keycloakUpdateUser;
+ keycloakGetAccessTokenAPI: typeof keycloakGetAccessTokenAPI;
+ keycloakCreateUserAPI: typeof keycloakCreateUserAPI;
+ keycloakResetPasswordAPI: typeof keycloakResetPasswordAPI;
+ keycloakGetUserAPI: typeof keycloakGetUserAPI;
+ keycloakDeleteUserAPI: typeof keycloakDeleteUserAPI;
+ keycloakUpdateUserAPI: typeof keycloakUpdateUserAPI;
+ keycloakDeleteSessionAPI: typeof keycloakDeleteSessionAPI;
+ keycloakGetUserSessionsAPI: typeof keycloakGetUserSessionsAPI;
+ keycloakDeleteUserSessions: typeof keycloakDeleteUserSessions;
+ keycloakResetUsers: typeof keycloakResetUsers;
+ keycloakCreateUser: typeof keycloakCreateUser;
+ keycloakSuspendUser(userEmail: string): ChainableT;
+ keycloakUnsuspendUser: typeof keycloakUnsuspendUser;
+ checkKeycloakLoginPage: typeof checkKeycloakLoginPage;
+ doKeycloakLogin(user): ChainableT;
+ verifyKeycloakLoginFailed: typeof verifyKeycloakLoginFailed;
+ }
+ }
+}
diff --git a/e2e-tests/cypress/tests/support/okta_commands.ts b/e2e-tests/cypress/tests/support/okta_commands.ts
index 19de9c8100..f281f1f0b5 100644
--- a/e2e-tests/cypress/tests/support/okta_commands.ts
+++ b/e2e-tests/cypress/tests/support/okta_commands.ts
@@ -29,10 +29,10 @@ interface User {
password: string;
}
-interface UserCollection {
- admins: User[];
- guests: User[];
- regulars: User[];
+export interface UserCollection {
+ admins: Record>;
+ guests: Record>;
+ regulars: Record>;
}
interface OktaResponse {
@@ -362,7 +362,7 @@ declare global {
oktaAddUsers: typeof oktaAddUsers;
oktaRemoveUsers: typeof oktaRemoveUsers;
checkOktaLoginPage: typeof checkOktaLoginPage;
- doOktaLogin: typeof doOktaLogin;
+ doOktaLogin(user: User): ChainableT;
}
}
}
diff --git a/e2e-tests/cypress/tests/support/saml_commands.ts b/e2e-tests/cypress/tests/support/saml_commands.ts
index 03786f53a6..17a4e2ec06 100644
--- a/e2e-tests/cypress/tests/support/saml_commands.ts
+++ b/e2e-tests/cypress/tests/support/saml_commands.ts
@@ -51,7 +51,7 @@ function doSamlLogin(settings) {
cy.checkLoginPage(settings);
//click the login button
- cy.findByText(settings.loginButtonText).should('be.visible').click().wait(TIMEOUTS.ONE_SEC);
+ return cy.findByText(settings.loginButtonText).should('be.visible').click().wait(TIMEOUTS.ONE_SEC);
}
Cypress.Commands.add('doSamlLogin', doSamlLogin);
@@ -65,7 +65,7 @@ function doSamlLogout(settings) {
// # Logout then check login page
cy.uiLogout();
- cy.checkLoginPage(settings);
+ return cy.checkLoginPage(settings);
}
Cypress.Commands.add('doSamlLogout', doSamlLogout);
@@ -99,14 +99,14 @@ Cypress.Commands.add('getInvitePeopleLink', getInvitePeopleLink);
* @param {AdminConfig} config - The config object
* @returns {TestSettings} - The settings to use for SAML tests
*/
-function setTestSettings(loginButtonText: string, config: AdminConfig): TestSettings {
- return {
+function setTestSettings(loginButtonText: string, config: AdminConfig): ChainableT {
+ return cy.wrap({
loginButtonText,
siteName: config.TeamSettings.SiteName,
siteUrl: config.ServiceSettings.SiteURL,
teamName: '',
user: null,
- };
+ });
}
Cypress.Commands.add('setTestSettings', setTestSettings);