test: 💍 Add admin UI test for change passsword flow (#2920)
* test: 💍 Add admin UI test for change passsword flow This PR adds a test to the Admin UI e2e test suite to validate the workflow of changing user password from settings menu ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17173 * refactor: 💡 create resources via REST API ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17173 * refactor: 💡 consistent code structure, comments ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17173 * refactor: 💡 code structure consistency ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17173 * refactor: 💡 removed version parameter from boundary-http funcs ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17173 * refactor: 💡 rm unneeded params from https makeAuthMethodPrimary ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17173 * refactor: 💡 rm ui login step ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17173
This commit is contained in:
6 files changed
+236
No files matched your search
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { test } from '../../global-setup.js';
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import * as boundaryCli from '../../helpers/boundary-cli';
|
||||
import * as boundaryHttp from '../../helpers/boundary-http.js';
|
||||
import { LoginPage } from '../pages/login.js';
|
||||
|
||||
// Reset storage state for this file to avoid being authenticated
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
|
||||
test.beforeAll(async () => {
|
||||
await boundaryCli.checkBoundaryCli();
|
||||
});
|
||||
|
||||
test(
|
||||
'Verify user can change password',
|
||||
{ tag: ['@ce', '@ent', '@aws', '@docker'] },
|
||||
async ({ page, request }) => {
|
||||
await page.goto('/');
|
||||
let org;
|
||||
let authMethod;
|
||||
try {
|
||||
let account;
|
||||
let user;
|
||||
|
||||
// Create a new org, password auth method, account, and user via http API
|
||||
org = await boundaryHttp.createOrg(request);
|
||||
|
||||
authMethod = await boundaryHttp.createPasswordAuthMethod(request, org.id);
|
||||
const username = 'test-user';
|
||||
const password = 'password';
|
||||
account = await boundaryHttp.createPasswordAccount(request, {
|
||||
authMethodId: authMethod.id,
|
||||
username,
|
||||
password,
|
||||
});
|
||||
await boundaryHttp.makeAuthMethodPrimary(request, {
|
||||
org,
|
||||
authMethodId: authMethod.id,
|
||||
});
|
||||
|
||||
user = await boundaryHttp.createUser(request, org.id);
|
||||
await boundaryHttp.addAccountsToUser(request, {
|
||||
user,
|
||||
accountIds: [account.id],
|
||||
});
|
||||
|
||||
// Log in using new account
|
||||
await page.reload();
|
||||
await page.getByText('Choose a different scope').click();
|
||||
await page.getByRole('link', { name: org.name }).click();
|
||||
await page.getByRole('link', { name: authMethod.name }).click();
|
||||
const loginPage = new LoginPage(page);
|
||||
await loginPage.login(username, password);
|
||||
await expect(
|
||||
page
|
||||
.getByRole('navigation', { name: 'breadcrumbs' })
|
||||
.getByText('Projects'),
|
||||
).toBeVisible();
|
||||
|
||||
// Change password for user
|
||||
await page.getByRole('button', { name: username }).click();
|
||||
await page.getByRole('link', { name: 'Change Password' }).click();
|
||||
const newPassword = 'new-password';
|
||||
await page.getByLabel('Current Password').fill(password);
|
||||
await page.getByLabel('New Password').fill(newPassword);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
await expect(
|
||||
page.getByRole('alert').getByText('Success', { exact: true }),
|
||||
).toBeVisible();
|
||||
|
||||
// Confirm user cannot log in with old password
|
||||
await loginPage.logout(username);
|
||||
await page.getByText('Choose a different scope').click();
|
||||
await page.getByRole('link', { name: org.name }).click();
|
||||
await page.getByRole('link', { name: authMethod.name }).click();
|
||||
await page.getByLabel('Login Name').fill(username);
|
||||
await page.getByLabel('Password', { exact: true }).fill(password);
|
||||
await page.getByRole('button', { name: 'Sign In' }).click();
|
||||
await expect(page.getByRole('alert').getByText('Error')).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible();
|
||||
|
||||
// Confirm user can log in with new password
|
||||
await loginPage.login(username, newPassword);
|
||||
await expect(
|
||||
page
|
||||
.getByRole('navigation', { name: 'breadcrumbs' })
|
||||
.getByText('Projects'),
|
||||
).toBeVisible();
|
||||
} finally {
|
||||
if (authMethod.id) {
|
||||
await boundaryHttp.deleteAuthMethod(request, authMethod.id);
|
||||
}
|
||||
if (org.id) {
|
||||
await boundaryHttp.deleteOrg(request, org.id);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -11,3 +11,6 @@ export * from './boundary-http/hosts.js';
|
||||
export * from './boundary-http/credential-stores.js';
|
||||
export * from './boundary-http/credentials.js';
|
||||
export * from './boundary-http/credential-libraries.js';
|
||||
export * from './boundary-http/auth-methods.js';
|
||||
export * from './boundary-http/accounts.js';
|
||||
export * from './boundary-http/users.js';
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import { checkResponse } from './responseHelper.js';
|
||||
|
||||
/**
|
||||
* Creates a new password auth account
|
||||
* @param {import('@playwright/test').APIRequestContext} request
|
||||
* @param {string} authMethodId
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @returns {Promise<Serializable>}
|
||||
*/
|
||||
export async function createPasswordAccount(
|
||||
request,
|
||||
{ authMethodId, username, password },
|
||||
) {
|
||||
const response = await request.post(`/v1/accounts`, {
|
||||
data: {
|
||||
attributes: {
|
||||
login_name: username,
|
||||
password: password,
|
||||
},
|
||||
auth_method_id: authMethodId,
|
||||
description: 'Generated via API from automated test',
|
||||
name: `Account ${nanoid()}`,
|
||||
type: 'password',
|
||||
},
|
||||
});
|
||||
return checkResponse(response);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import { checkResponse } from './responseHelper.js';
|
||||
|
||||
/**
|
||||
* Creates a new auth method
|
||||
* @param {import('@playwright/test').APIRequestContext} request
|
||||
* @param {string} orgId
|
||||
* @returns {Promise<Serializable>}
|
||||
*/
|
||||
export async function createPasswordAuthMethod(request, orgId) {
|
||||
const response = await request.post(`/v1/auth-methods`, {
|
||||
data: {
|
||||
scope_id: orgId,
|
||||
name: `Auth Method ${nanoid()}`,
|
||||
description: 'Generated via API from automated test',
|
||||
type: 'password',
|
||||
},
|
||||
});
|
||||
return checkResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an auth method
|
||||
* @param {import('@playwright/test').APIRequestContext} request
|
||||
* @param {string} authMethodId
|
||||
* @returns {Promise<Serializable>}
|
||||
*/
|
||||
export async function deleteAuthMethod(request, authMethodId) {
|
||||
const response = await request.delete(`/v1/auth-methods/${authMethodId}`);
|
||||
return checkResponse(response, true);
|
||||
}
|
||||
@@ -48,3 +48,21 @@ export async function createProject(request, scopeId) {
|
||||
});
|
||||
return checkResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make Auth Method Primary
|
||||
* @param {import('@playwright/test').APIRequestContext} request
|
||||
* @param {string} orgId
|
||||
* @param {string} authMethodId
|
||||
* @returns {Promise<Serializable>}
|
||||
*/
|
||||
export async function makeAuthMethodPrimary(request, { org, authMethodId }) {
|
||||
const response = await request.patch(`v1/scopes/${org.id}`, {
|
||||
data: {
|
||||
primary_auth_method_id: authMethodId,
|
||||
version: org.version,
|
||||
},
|
||||
});
|
||||
|
||||
return checkResponse(response);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import { checkResponse } from './responseHelper.js';
|
||||
|
||||
/**
|
||||
* Create a new user
|
||||
* @param {import('@playwright/test').APIRequestContext} request
|
||||
* @param {string} orgId
|
||||
* @returns {Promise<Serializable>}
|
||||
*/
|
||||
export async function createUser(request, orgId) {
|
||||
const response = await request.post(`/v1/users`, {
|
||||
data: {
|
||||
scope_id: orgId,
|
||||
name: `User ${nanoid()}`,
|
||||
description: 'Generated via API from automated test',
|
||||
},
|
||||
});
|
||||
return checkResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add account to user
|
||||
* @param {import('@playwright/test').APIRequestContext} request
|
||||
* @param {string} userId
|
||||
* @param {string} accountId
|
||||
* @returns {Promise<Serializable>}
|
||||
*/
|
||||
export async function addAccountsToUser(request, { user, accountIds }) {
|
||||
const response = await request.post(`/v1/users/${user.id}:add-accounts`, {
|
||||
data: {
|
||||
account_ids: accountIds,
|
||||
version: user.version,
|
||||
},
|
||||
});
|
||||
return checkResponse(response);
|
||||
}
|
||||
Reference in new issue
Block a user