Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions e2etests/api-requests/restapi-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class RestAPIRequest extends BaseRequest {
readonly employeesEndpoint: string;
readonly organizationConstraintsEndpoint: string;
readonly policiesEndpoint: string;
readonly taggingPoliciesEndpoint: string;
readonly poolsEndpoint: string;

/**
Expand All @@ -25,6 +26,7 @@ export class RestAPIRequest extends BaseRequest {
this.employeesEndpoint = `${baseUrl}/restapi/v2/employees`;
this.organizationConstraintsEndpoint = `${baseUrl}/restapi/v2/organization_constraints`;
this.policiesEndpoint = `${this.organizationsEndpoint}/${process.env.DEFAULT_ORG_ID}/organization_constraints?hit_days=3&type=resource_quota&type=recurring_budget&type=expiring_budget`;
this.taggingPoliciesEndpoint = `${this.organizationsEndpoint}/${process.env.DEFAULT_ORG_ID}/organization_constraints?hit_days=3&type=tagging_policy`;
this.poolsEndpoint = `${baseUrl}/restapi/v2/pools`;
}

Expand Down Expand Up @@ -107,14 +109,13 @@ export class RestAPIRequest extends BaseRequest {
*/
async deletePolicy(policyID: string, token: string): Promise<void> {
const endpoint = `${this.organizationConstraintsEndpoint}/${policyID}`;
debugLog(`Deleting anomaly policy ${policyID}`);
const response = await this.request.delete(endpoint, {
headers: getBearerTokenHeader(token),
});
if (response.status() !== 204) {
throw new Error(`[ERROR] Failed to delete anomaly policy ID: ${policyID}`);
}
debugLog(`Anomaly policy ${policyID} deleted`);
debugLog(`Policy ${policyID} deleted`);
}

/**
Expand All @@ -131,6 +132,14 @@ export class RestAPIRequest extends BaseRequest {
});
}

async getTaggingPolicies(token: string): Promise<APIResponse> {
const endpoint = this.taggingPoliciesEndpoint;

return await this.request.get(endpoint, {
headers: getBearerTokenHeader(token),
});
}

async getPoolsResponse(token: string): Promise<APIResponse> {
const endpoint = `${this.poolsEndpoint}/${getEnvironmentParentPoolId()}?children=true&details=true`;
const headers = getBearerTokenHeader(token);
Expand Down
135 changes: 135 additions & 0 deletions e2etests/mocks/tagging-policy-page.mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
export const TaggingPolicyViolationResponse = {
organization_constraints: [
{
deleted_at: 0,
id: '1dc38049-b397-45ff-b786-6f42c6b60f12',
created_at: 1769696555,
name: 'Required Tag Policy 1769696547422',
organization_id: '4eae08f8-9b40-4094-a11c-f9ee2dc76a12',
type: 'tagging_policy',
definition: {
start_date: 1769644800,
conditions: {
without_tag: 'AccountId',
},
},
filters: {},
last_run: 1769696702,
last_run_result: {
value: 3185,
},
limit_hits: [
{
deleted_at: 0,
id: '6dea591a-ec7b-4eba-bbf0-4444e0773f4a',
organization_id: '4eae08f8-9b40-4094-a11c-f9ee2dc76a12',
constraint_id: '1dc38049-b397-45ff-b786-6f42c6b60f12',
constraint_limit: 0.0,
value: 3185.0,
created_at: 1769696702,
run_result: {
value: 3185,
},
},
],
},
{
deleted_at: 0,
id: '40c24616-6016-454e-be6f-ca4c165f8ceb',
created_at: 1769696597,
name: 'Correlated Tag Policy 1769696585306',
organization_id: '4eae08f8-9b40-4094-a11c-f9ee2dc76a12',
type: 'tagging_policy',
definition: {
start_date: 1769644800,
conditions: {
tag: 'CostCenter',
without_tag: 'Environment',
},
},
filters: {},
last_run: 1769696702,
last_run_result: {
value: 1,
},
limit_hits: [
{
deleted_at: 0,
id: '8032d27f-81c3-4eaa-b0c3-791d53d8ae36',
organization_id: '4eae08f8-9b40-4094-a11c-f9ee2dc76a12',
constraint_id: '40c24616-6016-454e-be6f-ca4c165f8ceb',
constraint_limit: 0.0,
value: 1.0,
created_at: 1769696702,
run_result: {
value: 1,
},
},
],
},
{
deleted_at: 0,
id: '62f013ef-748e-4d06-860b-3f530b5c685f',
created_at: 1769696578,
name: 'Prohibited Tag Policy 1769696563199',
organization_id: '4eae08f8-9b40-4094-a11c-f9ee2dc76a12',
type: 'tagging_policy',
definition: {
start_date: 1769644800,
conditions: {
tag: '__department',
},
},
filters: {
active: [true],
},
last_run: 1769696702,
last_run_result: {
value: 2,
},
limit_hits: [
{
deleted_at: 0,
id: '77a91b69-d183-4f46-9b5e-d1173e0fe031',
organization_id: '4eae08f8-9b40-4094-a11c-f9ee2dc76a12',
constraint_id: '62f013ef-748e-4d06-860b-3f530b5c685f',
constraint_limit: 0.0,
value: 2.0,
created_at: 1769696702,
run_result: {
value: 2,
},
},
],
},
{
"deleted_at": 0,
"id": "e26bca14-4a38-49dc-a9cc-0798406cc6e4",
"created_at": 1769697639,
"name": "Non-violating Correlated Tag Policy",
"organization_id": "4eae08f8-9b40-4094-a11c-f9ee2dc76a12",
"type": "tagging_policy",
"definition": {
"start_date": 1769697608,
"conditions": {
"tag": "CostCenter",
"without_tag": "Environment"
}
},
"filters": {
"cloud_account": [
{
"id": "ec8b9ca5-6d16-465e-8831-472a6bcd5fcf",
"name": "Marketplace (Dev)",
"type": "aws_cnr"
}
]
},
"last_run": 1769697902,
"last_run_result": {
"value": 0
},
"limit_hits": []
}
],
};
69 changes: 68 additions & 1 deletion e2etests/pages/base-create-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export abstract class BaseCreatePage extends BasePage {
readonly saveBtn: Locator;
readonly cancelBtn: Locator;

readonly setDateBtn: Locator;
readonly timePicker: Locator;
readonly amButton: Locator;
readonly pmButton: Locator;
readonly setButton: Locator;

/**
* Initializes a new instance of the BaseCreatePage class.
* @param {Page} page - The Playwright page object.
Expand All @@ -57,7 +63,7 @@ export abstract class BaseCreatePage extends BasePage {
this.filtersBox = this.main.locator('xpath=(//div[.="Filters:"])[1]/..');
this.allFilterBoxButtons = this.filtersBox.locator('button');
this.filterPopover = this.page.locator('//div[contains(@id, "filter-popover")]');
this.filterApplyButton = this.filterPopover.getByRole('button' , { name: 'Apply' });
this.filterApplyButton = this.filterPopover.getByRole('button', { name: 'Apply' });

this.suggestionsFilter = this.filtersBox.getByRole('button', { name: 'Suggestions' });
this.dataSourceFilter = this.filtersBox.getByRole('button', { name: 'Data source (' });
Expand All @@ -84,5 +90,66 @@ export abstract class BaseCreatePage extends BasePage {
this.showLessFiltersBtn = this.main.getByRole('button', { name: 'Show less' });
this.saveBtn = this.main.getByTestId('btn_create');
this.cancelBtn = this.main.getByTestId('btn_cancel');

this.setDateBtn = this.main.getByTestId('btn_select_date');
this.timePicker = this.page.locator('//input[@data-test-id="half-hour-time-selector"]/..');
this.amButton = this.page.getByRole('button', { name: 'AM' });
this.pmButton = this.page.getByRole('button', { name: 'PM' });
this.setButton = this.page.getByRole('button', { name: 'Set' });
}

/**
* Sets the time for the policy.
*
* @param {string} [time='12:00'] - The time to set in the format 'hh:mm'.
* @param {boolean} [am=true] - Whether to set the time as AM (true) or PM (false).
* @returns {Promise<void>} A promise that resolves when the time is set.
*/
protected async setTime(time: string = '12:00', am: boolean = true): Promise<void> {
await this.setDateBtn.click();
await this.selectFromComboBox(this.timePicker, time);
if (am) {
await this.amButton.click();
} else {
await this.pmButton.click();
}
await this.setButton.click();
}

/**
* Selects a filter and applies the specified filter option.
*
* @param {Locator} filter - The filter locator to select.
* @param {string} filterOption - The specific filter option to apply.
* @throws {Error} Throws an error if `filterOption` is not provided when `filter` is specified.
* @returns {Promise<void>} A promise that resolves when the filter is applied.
*/
protected async selectFilter(filter: Locator, filterOption: string): Promise<void> {
if (filter) {
if (!filterOption) {
throw new Error('filterOption must be provided when filter is specified');
}
if (!(await filter.isVisible())) await this.showMoreFiltersBtn.click();
await filter.click();

await this.filterPopover.getByLabel(filterOption).click();
await this.filterApplyButton.click();
}
}

/**
* Selects a filter by its text and applies the specified filter option.
*
* This method locates a filter button within the filters box by matching its name
* with the provided `filter` parameter. It then applies the specified `filterOption`
* to the selected filter.
*
* @param {string} filter - The name of the filter to select.
* @param {string} filterOption - The specific filter option to apply.
* @returns {Promise<void>} A promise that resolves when the filter is applied.
*/
async selectFilterByText(filter: string, filterOption: string): Promise<void> {
const filterLocator = this.filtersBox.getByRole('button', { name: new RegExp(`^${filter}`) });
await this.selectFilter(filterLocator, filterOption);
}
}
3 changes: 1 addition & 2 deletions e2etests/pages/base-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export abstract class BasePage {
readonly progressBar: Locator;
readonly tooltip: Locator;
readonly table: Locator;

readonly infoColor: string; // Default color for neutral state
readonly warningColor: string; // Default color for warning state
readonly errorColor: string; // Default color for error state
Expand All @@ -30,14 +29,14 @@ export abstract class BasePage {
this.page = page;
this.url = url;
this.main = this.page.locator('main');
this.table = this.main.locator('table');
this.loadingPageImg = this.page.getByRole('img', { name: 'Loading page' });
this.progressBar = this.page.locator('//main[@id="mainLayoutWrapper"]//*[@role="progressbar"]');
this.tooltip = this.page.getByRole('tooltip');
this.infoColor = 'rgb(0, 0, 0)'; // Default color for neutral state
this.warningColor = 'rgb(232, 125, 30)'; // Default color for warning state
this.errorColor = 'rgb(187, 20, 37)'; // Default color for error state
this.successColor = 'rgb(0, 120, 77)'; // Default color for success state
this.table = this.main.locator('table');
}

/**
Expand Down
8 changes: 4 additions & 4 deletions e2etests/pages/date-picker-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class DatePickerPage extends BasePage {
* @param {boolean} [wait=true] - Whether to wait for the page loader to disappear and the canvas to load after applying the date range.
* @returns {Promise<void>} A promise that resolves when the operation is complete.
*/
async selectLast7DaysDateRange(wait = true): Promise<void> {
async selectLast7DaysDateRange(wait: boolean = true): Promise<void> {
await this.selectDateBtn.click();
await this.last7DaysBtn.click();
await this.applyDateBtn.click();
Expand All @@ -78,7 +78,7 @@ export class DatePickerPage extends BasePage {
* @param {boolean} [wait=true] - Whether to wait for the page loader to disappear and the canvas to load after applying the date range.
* @returns {Promise<void>} A promise that resolves when the operation is complete.
*/
async selectLast30DaysDateRange(wait = true): Promise<void> {
async selectLast30DaysDateRange(wait: boolean = true): Promise<void> {
await this.selectDateBtn.click();
await this.last30DaysBtn.click();
await this.applyDateBtn.click();
Expand All @@ -95,7 +95,7 @@ export class DatePickerPage extends BasePage {
* @param {boolean} [wait=true] - Whether to wait for the page loader to disappear and the canvas to load after applying the date range.
* @returns {Promise<void>} A promise that resolves when the operation is complete.
*/
async selectLastMonthDateRange(wait = true): Promise<void> {
async selectLastMonthDateRange(wait: boolean = true): Promise<void> {
await this.selectDateBtn.click();
await this.lastMonthBtn.click();
await this.applyDateBtn.click();
Expand All @@ -112,7 +112,7 @@ export class DatePickerPage extends BasePage {
* @param {boolean} [wait=true] - Whether to wait for the page loader to disappear and the canvas to load after applying the date range.
* @returns {Promise<void>} A promise that resolves when the operation is complete.
*/
async selectThisMonthDateRange(wait = true): Promise<void> {
async selectThisMonthDateRange(wait: boolean = true): Promise<void> {
await this.selectDateBtn.click();
await this.thisMonthBtn.click();
await this.applyDateBtn.click();
Expand Down
49 changes: 0 additions & 49 deletions e2etests/pages/policies-create-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ export class PoliciesCreatePage extends BaseCreatePage {
readonly resourceCountInput: Locator;
readonly monthlyBudgetInput: Locator;
readonly totalBudgetInput: Locator;
readonly setDateBtn: Locator;
readonly timePicker: Locator;
readonly amButton: Locator;
readonly pmButton: Locator;
readonly setButton: Locator;

/**
* Initializes a new instance of the PoliciesCreatePage class.
Expand All @@ -26,11 +21,6 @@ export class PoliciesCreatePage extends BaseCreatePage {
this.resourceCountInput = this.main.getByTestId('input_maxValue');
this.monthlyBudgetInput = this.main.getByTestId('input_monthlyBudget');
this.totalBudgetInput = this.main.getByTestId('input_totalBudget');
this.setDateBtn = this.main.getByTestId('btn_select_date');
this.timePicker = this.page.locator('//input[@data-test-id="half-hour-time-selector"]/..');
this.amButton = this.page.getByRole('button', { name: 'AM' });
this.pmButton = this.page.getByRole('button', { name: 'PM' });
this.setButton = this.page.getByRole('button', { name: 'Set' });
}

/**
Expand Down Expand Up @@ -90,43 +80,4 @@ export class PoliciesCreatePage extends BaseCreatePage {
await this.selectFilter(filter, filterOption);
await this.saveBtn.click();
}

/**
* Sets the time for the policy.
*
* @param {string} [time='12:00'] - The time to set in the format 'hh:mm'.
* @param {boolean} [am=true] - Whether to set the time as AM (true) or PM (false).
* @returns {Promise<void>} A promise that resolves when the time is set.
*/
async setTime(time: string = '12:00', am: boolean = true): Promise<void> {
await this.setDateBtn.click();
await this.selectFromComboBox(this.timePicker, time);
if (am) {
await this.amButton.click();
} else {
await this.pmButton.click();
}
await this.setButton.click();
}

/**
* Selects a filter and applies the specified filter option.
*
* @param {Locator} filter - The filter locator to select.
* @param {string} filterOption - The specific filter option to apply.
* @throws {Error} Throws an error if `filterOption` is not provided when `filter` is specified.
* @returns {Promise<void>} A promise that resolves when the filter is applied.
*/
private async selectFilter(filter: Locator, filterOption: string): Promise<void> {
if (filter) {
if (!filterOption) {
throw new Error('filterOption must be provided when filter is specified');
}
if (!(await filter.isVisible())) await this.showMoreFiltersBtn.click();
await filter.click();

await this.filterPopover.getByLabel(filterOption).click();
await this.filterApplyButton.click();
}
}
}
Loading