Skip to content

Commit

Permalink
Merge pull request #57 from intuitem/CA-189-add-tests-for-the-User-tab
Browse files Browse the repository at this point in the history
CA-189-add-tests-for-the-User-tab
  • Loading branch information
nas-tabchiche authored Feb 16, 2024
2 parents b1286e0 + 287280b commit 3f925e3
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 34 deletions.
2 changes: 1 addition & 1 deletion frontend/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const config: PlaywrightTestConfig = {
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 1,
workers: process.env.CI ? 1 : 2,
workers: process.env.CI ? 1 : 1,
globalTimeout: 60 * 60 * 1000,
timeout: 50 * 1000,
expect : {
Expand Down
74 changes: 62 additions & 12 deletions frontend/tests/e2e-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ SCRIPT_LONG_ARGS=()
SCRIPT_SHORT_ARGS=()
TEST_PATHS=()

if lsof -Pi :8080 -sTCP:LISTEN -t > /dev/null ; then
echo "The port 8080 is already in use!"
echo "Please stop the running process using the port and try again."
exit 1
fi
BACKEND_PORT=8080

for arg in "$@"
do
if [[ $arg == --* ]]; then
if [[ $arg == --port=* ]]; then
BACKEND_PORT="${arg#*=}"
elif [[ $arg == --* ]]; then
SCRIPT_LONG_ARGS+=("$arg")
elif [[ $arg == -* ]]; then
SCRIPT_SHORT_ARGS+=("$arg")
Expand All @@ -24,6 +22,53 @@ do
fi
done

if [[ " ${SCRIPT_SHORT_ARGS[@]} " =~ " -h " ]] || [[ " ${SCRIPT_LONG_ARGS[@]} " =~ " --help " ]]; then
echo "Usage: e2e-tests.sh [options] [test_path]"
echo "Run the end-to-end tests for the CISO Assistant application."
echo "Options:"
echo " --browser=NAME Run the tests in the specified browser (chromium, firefox, webkit)"
echo " --global-timeout=MS Maximum time this test suite can run in milliseconds (default: unlimited)"
echo " --headed Run the tests in headful mode"
echo " -h Show this help message and exit"
echo " --list List all the tests"
echo " --port=PORT Run the backend server on the specified port (default: 8080)"
echo " --project=NAME Run the tests in the specified project"
echo " -q Quick mode: execute only the tests 1 time with no retries and only 1 project"
echo " --repeat-each=COUNT Run the tests the specified number of times (default: 1)"
echo " --retries=COUNT Set the number of retries for the tests"
echo " --timeout=MS Set the timeout for the tests in milliseconds"
echo " -v Show the output of the backend server"
echo " --workers=COUNT Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 1)"
exit 0
fi

if command -v ss >/dev/null 2>&1; then
# Use ss if it's available
if ss -tuln | grep -q :$BACKEND_PORT ; then
echo "The port $BACKEND_PORT is already in use!"
echo "Please stop the running process using the port or change the backend test server port using --port=PORT and try again."
exit 1
fi
elif command -v netstat >/dev/null 2>&1; then
# Use netstat if it's available
if netstat -tuln | grep -q :$BACKEND_PORT ; then
echo "The port $BACKEND_PORT is already in use!"
echo "Please stop the running process using the port or change the backend test server port using --port=PORT and try again."
exit 1
fi
else
if [[ $EUID > 0 ]] ; then
echo "WARNING: Running the script without root permissions may prevent the tests from running properly."
echo "Consider to install either ss or netstat on this system to perform the port check without root privileges."
read -n 1 -s -r -p "Press any key to continue anyway..."
echo ""
elif lsof -Pi :$BACKEND_PORT -sTCP:LISTEN -t > /dev/null ; then
echo "The port $BACKEND_PORT is already in use!"
echo "Please stop the running process using the port or change the backend test server port using --port=PORT and try again."
exit 1
fi
fi

cleanup() {
echo -e "\nCleaning up..."
if [ -n $BACKEND_PID ] ; then
Expand Down Expand Up @@ -73,17 +118,18 @@ cd $APP_DIR/backend/
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser --noinput
if [[ " ${SCRIPT_SHORT_ARGS[@]} " =~ " -v " ]]; then
nohup python manage.py runserver 8080 > $APP_DIR/frontend/tests/utils/.testbackendoutput.out 2>&1 &
if [[ " ${SCRIPT_SHORT_ARGS[@]} " =~ " -v " ]] ; then
nohup python manage.py runserver $BACKEND_PORT > $APP_DIR/frontend/tests/utils/.testbackendoutput.out 2>&1 &
echo "You can view the backend server output at $APP_DIR/frontend/tests/utils/.testbackendoutput.out"
else
nohup python manage.py runserver 8080 > /dev/null 2>&1 &
nohup python manage.py runserver $BACKEND_PORT > /dev/null 2>&1 &
fi
BACKEND_PID=$!
echo "test backend server started on port 8080 (PID: $BACKEND_PID)"
echo "test backend server started on port $BACKEND_PORT (PID: $BACKEND_PID)"

echo "starting playwright tests"
export ORIGIN=http://localhost:4173
export PUBLIC_BACKEND_API_URL=http://localhost:8080/api
export PUBLIC_BACKEND_API_URL=http://localhost:$BACKEND_PORT/api

cd $APP_DIR/frontend/

Expand All @@ -98,4 +144,8 @@ else
echo "with args: ${SCRIPT_LONG_ARGS[@]}"
fi

npx playwright test ./tests/functional/"${TEST_PATHS[@]}" "${SCRIPT_LONG_ARGS[@]}"
if [[ " ${SCRIPT_SHORT_ARGS[@]} " =~ " -q " ]] ; then
npx playwright test ./tests/functional/"${TEST_PATHS[@]}" -x --project=chromium "${SCRIPT_LONG_ARGS[@]}"
else
npx playwright test ./tests/functional/"${TEST_PATHS[@]}" "${SCRIPT_LONG_ARGS[@]}"
fi
8 changes: 4 additions & 4 deletions frontend/tests/functional/detailed/common.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ for (const key of testPages) {
await pages[key].waitUntilLoaded();
await pages[key].createItem(items[key].build, "dependency" in items[key] ? items[key].dependency : null);

if (await pages[key].getRow(items[key].build.name).isHidden()) {
await pages[key].searchInput.fill(items[key].build.name);
if (await pages[key].getRow(items[key].build.name || items[key].build.email).isHidden()) {
await pages[key].searchInput.fill(items[key].build.name || items[key].build.email);
}

await pages[key].waitUntilLoaded();
await pages[key].viewItemDetail(items[key].build.name);
await pages[key].viewItemDetail(items[key].build.name || items[key].build.email);
await pages[key].itemDetail.hasTitle();
//wait fore the file to load to prevent crashing
page.url().includes('evidences') ? await pages[key].page.getByTestId("attachment-name-title").waitFor({state: 'visible'}) : null;
Expand All @@ -58,7 +58,7 @@ for (const key of testPages) {

test(`user can edit ${items[key].displayName.toLowerCase()} item`, async ({ pages, page }, testInfo) => {
const editedValues = await pages[key].itemDetail.editItem(items[key].build, items[key].editParams);
replaceValues(history[testInfo.line], items[key].build.name, items[key].build.name + ' edited');
replaceValues(history[testInfo.line], items[key].build.name || items[key].build.email, items[key].build.name + ' edited' || '_' + items[key].build.email);
//wait fore the file to load to prevent crashing
page.url().includes('evidences') ? await pages[key].page.getByTestId("attachment-name-title").waitFor({state: 'visible'}) : null;

Expand Down
20 changes: 19 additions & 1 deletion frontend/tests/functional/user-route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,13 +233,31 @@ test('user usual routine actions are working correctly', async ({

//TODO assert that the risk acceptance data are displayed in the table
});

await test.step('user can add another user', async () => {
await sideBar.click('Organization', pages.usersPage.url);
await expect(page).toHaveURL(pages.usersPage.url);
await pages.usersPage.hasTitle();

await pages.usersPage.createItem({
email: vars.user.email
});

//TODO assert that the user data are displayed in the table
});
});

test.afterEach('cleanup', async ({ foldersPage, page }) => {
test.afterEach('cleanup', async ({ foldersPage, usersPage, page }) => {
await foldersPage.goto();
await page.waitForURL(foldersPage.url);
await foldersPage.deleteItemButton(vars.folderName).click();
await foldersPage.deleteModalConfirmButton.click();
await expect(foldersPage.getRow(vars.folderName)).not.toBeVisible();

await usersPage.goto();
await page.waitForURL(usersPage.url);
await usersPage.deleteItemButton(vars.user.email).click();
await usersPage.deleteModalConfirmButton.click();
await expect(usersPage.getRow(vars.user.email)).not.toBeVisible();
});

7 changes: 2 additions & 5 deletions frontend/tests/utils/form-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,15 @@ export class FormContent {
}

async fill(values: { [k: string]: any }) {
let temp = {};

for (const key in values) {
const field = this.fields.get(key);

switch (field?.type) {
case FormFieldType.CHECKBOX:
if (values[key] === "true") {
if (values[key]) {
await field.locator.check();
}
else if (values[key] === "false") {
else {
await field.locator.uncheck();
}
break;
Expand Down Expand Up @@ -84,7 +82,6 @@ export class FormContent {
await field?.locator.fill(values[key]);
break;
}
// await this.page.waitForTimeout(20);
}
}

Expand Down
7 changes: 6 additions & 1 deletion frontend/tests/utils/page-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ export class PageContent extends BasePage {
await this.form.saveButton.click();
await expect(this.form.formTitle).not.toBeVisible();
if (typeof this.name == 'string') {
await this.isToastVisible('Successfully created ' + this.name.substring(0, this.name.length - 1).toLowerCase() + /\..+/.source);
if (this.name === "Users") {
await this.isToastVisible('User successfully created' + /\..+/.source);
}
else {
await this.isToastVisible('Successfully created ' + this.name.substring(0, this.name.length - 1).toLowerCase() + /\..+/.source);
}
}
else {
await this.isToastVisible('Successfully created ' + this.name.source + /\..+/.source, 'i');
Expand Down
9 changes: 6 additions & 3 deletions frontend/tests/utils/page-detail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class PageDetail extends BasePage {
await this.form.fill(editedValues);
await this.form.saveButton.click();

await this.isToastVisible('.+ successfully saved: ' + {...buildParams, ...editedValues}.name);
await this.isToastVisible('.+ successfully saved: ' + ({...buildParams, ...editedValues}.name || {...buildParams, ...editedValues}.email));
return editedValues;
}

Expand All @@ -57,8 +57,11 @@ export class PageDetail extends BasePage {
for (const key in values) {
if (await this.page.getByTestId(key.replaceAll('_', '-') + "-field-title").isVisible()) {
await expect.soft(this.page.getByTestId(key.replaceAll('_', '-') + "-field-title")).toHaveText(new RegExp(key.replaceAll('_', ' '), 'i'));

if (this.form.fields.get(key)?.type === FormFieldType.DATE) {

if (this.form.fields.get(key)?.type === FormFieldType.CHECKBOX) {
await expect.soft(this.page.getByTestId(key.replaceAll('_', '-') + "-field-value")).toHaveText(values[key] ? "true" : "--");
}
else if (this.form.fields.get(key)?.type === FormFieldType.DATE) {
const displayedValue = await this.page.getByTestId(key.replaceAll('_', '-') + "-field-value").innerText();

const displayedDate = new Date(displayedValue);
Expand Down
12 changes: 12 additions & 0 deletions frontend/tests/utils/test-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ export default {
description: "Test description",
file: new URL('../utils/test_image.jpg', import.meta.url).pathname,
file2: new URL('../utils/test_file.txt', import.meta.url).pathname,
user: {
email: "[email protected]",
password: "password",
firstName: "Test",
lastName: "User",
},
usergroups: {
analyst: "Analyst",
auditor: "Auditor",
domainManager: "Domain manager",
validator: "Validator",
},
framework: {
name: "NIST CSF",
urn: "urn:intuitem:risk:library:nist-csf-1.1"
Expand Down
51 changes: 44 additions & 7 deletions frontend/tests/utils/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import testData from './test-data.js'
type Fixtures = {
sideBar: SideBar;
pages: {[page: string]: PageContent}
complianceAssessmentsPage: PageContent;
analyticsPage: AnalyticsPage;
assetsPage: PageContent;
complianceAssessmentsPage: PageContent;
evidencesPage: PageContent;
foldersPage: PageContent;
frameworksPage: PageContent;
Expand All @@ -24,7 +25,7 @@ type Fixtures = {
securityFunctionsPage: PageContent;
securityMeasuresPage: PageContent;
threatsPage: PageContent;
analyticsPage: AnalyticsPage;
usersPage: PageContent;
logedPage: LoginPage;
loginPage: LoginPage;
};
Expand All @@ -34,8 +35,8 @@ export const test = base.extend<Fixtures>({
await use(new SideBar(page));
},

pages: async ({ page, complianceAssessmentsPage, assetsPage, evidencesPage, foldersPage, frameworksPage, librariesPage, projectsPage, riskAcceptancesPage, riskAssessmentsPage, riskMatricesPage, riskScenariosPage, securityFunctionsPage, securityMeasuresPage, threatsPage }, use) => {
await use({complianceAssessmentsPage, assetsPage, evidencesPage, foldersPage, frameworksPage, librariesPage, projectsPage, riskAcceptancesPage, riskAssessmentsPage, riskMatricesPage, riskScenariosPage, securityFunctionsPage, securityMeasuresPage, threatsPage});
pages: async ({ page, complianceAssessmentsPage, assetsPage, evidencesPage, foldersPage, frameworksPage, librariesPage, projectsPage, riskAcceptancesPage, riskAssessmentsPage, riskMatricesPage, riskScenariosPage, securityFunctionsPage, securityMeasuresPage, threatsPage, usersPage }, use) => {
await use({complianceAssessmentsPage, assetsPage, evidencesPage, foldersPage, frameworksPage, librariesPage, projectsPage, riskAcceptancesPage, riskAssessmentsPage, riskMatricesPage, riskScenariosPage, securityFunctionsPage, securityMeasuresPage, threatsPage, usersPage});
},

complianceAssessmentsPage: async ({ page }, use) => {
Expand Down Expand Up @@ -186,6 +187,17 @@ export const test = base.extend<Fixtures>({
await use(tPage);
},

usersPage: async ({ page }, use) => {
const uPage = new PageContent(page, '/users', 'Users', [
{ name: 'email', type: type.TEXT },
{ name: 'first_name', type: type.TEXT },
{ name: 'last_name', type: type.TEXT },
{ name: 'user_groups', type: type.SELECT_MULTIPLE_AUTOCOMPLETE },
{ name: 'is_active', type: type.CHECKBOX },
]);
await use(uPage);
},

logedPage: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
Expand Down Expand Up @@ -263,6 +275,24 @@ export class TestContent {
description: ""
}
},
usersPage: {
displayName: "Users",
build : {
email: vars.user.email
},
editParams: {
email: '_' + vars.user.email,
first_name: vars.user.firstName,
last_name: vars.user.lastName,
user_groups: [
`${vars.folderName} - ${vars.usergroups.analyst}`,
`${vars.folderName} - ${vars.usergroups.auditor}`,
`${vars.folderName} - ${vars.usergroups.domainManager}`,
`${vars.folderName} - ${vars.usergroups.validator}`,
],
is_active: false
}
},
projectsPage: {
displayName: "Projects",
build: {
Expand Down Expand Up @@ -468,14 +498,21 @@ export class TestContent {
expiry_date: "2025-12-31",
//TODO add approver & risk_scenarios
}
}
},

}
}

static generateTestVars() {
const vars = {...testData};
const vars = structuredClone(testData);
for (const key in testData) {
vars[key] = key.match(/.*Name/) ? getUniqueValue(testData[key]) : testData[key];
if (key === 'user') {
const email = testData[key].email.split('@');
vars[key].email = getUniqueValue(email[0]) + '@' + email[1];
}
else if (key.match(/.*Name/)) {
vars[key] = getUniqueValue(testData[key]);
}
}
return vars
}
Expand Down

0 comments on commit 3f925e3

Please sign in to comment.