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
189 changes: 189 additions & 0 deletions EstateManagementUI.IntegrationTests/Common/DashboardPageHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public async Task OpenEstateInfoFromEntryAsync()
await RunWithFailureArtifactsAsync(async () =>
{
await _page.Locator("a[href='/estate-info']").ClickAsync(new LocatorClickOptions { NoWaitAfter = true });
await _page.WaitForURLAsync(new Regex(@".*/estate-info.*", RegexOptions.IgnoreCase));
await _page.WaitForLoadStateAsync(LoadState.NetworkIdle);
}, nameof(OpenEstateInfoFromEntryAsync));
}
Expand All @@ -67,6 +68,11 @@ await RunWithFailureArtifactsAsync(async () =>
State = WaitForSelectorState.Visible,
Timeout = 10000
});
await _page.GetByText("Comprehensive estate management and configuration").WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Timeout = 10000
});
await _page.Locator("#loginButton").WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Expand All @@ -79,6 +85,171 @@ await _page.Locator("#loginButton").WaitForAsync(new LocatorWaitForOptions
}, nameof(AssertEstateInfoPageVisibleAsync));
}

public async Task OpenOperatorManagementScreenAsync()
{
await RunWithFailureArtifactsAsync(async () =>
{
var operatorLink = _page.Locator("#operatorsLink");
if (await operatorLink.CountAsync() > 0 && await operatorLink.First.IsVisibleAsync())
{
await operatorLink.First.ClickAsync(new LocatorClickOptions { NoWaitAfter = true });
}
else
{
await _page.GotoAsync(ResolveEstateManagementBaseUrl() + "/operators");
}

await _page.WaitForLoadStateAsync(LoadState.NetworkIdle);
}, nameof(OpenOperatorManagementScreenAsync));
}

public async Task AssertOperatorManagementHeadingVisibleAsync()
{
await RunWithFailureArtifactsAsync(async () =>
{
var heading = _page.GetByRole(AriaRole.Heading, new() { Name = "Operator Management" });
await heading.WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Timeout = 10000
});

(await heading.IsVisibleAsync()).ShouldBeTrue();
}, nameof(AssertOperatorManagementHeadingVisibleAsync));
}

public async Task AssertOperatorListContainsAsync(string operatorName)
{
await RunWithFailureArtifactsAsync(async () =>
{
var operatorRow = GetOperatorRow(operatorName);
await operatorRow.WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Timeout = 10000
});

(await operatorRow.IsVisibleAsync()).ShouldBeTrue();
}, nameof(AssertOperatorListContainsAsync));
}

public async Task OpenOperatorViewAsync(string operatorName)
{
await RunWithFailureArtifactsAsync(async () =>
{
var operatorRow = GetOperatorRow(operatorName);
await operatorRow.WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Timeout = 10000
});

await operatorRow.ClickAsync();
}, nameof(OpenOperatorViewAsync));
}

public async Task AssertOperatorViewVisibleAsync(string operatorName)
{
await RunWithFailureArtifactsAsync(async () =>
{
var heading = _page.GetByRole(AriaRole.Heading, new() { Name = $"View Operator: {operatorName}" });
await heading.WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Timeout = 10000
});

(await heading.IsVisibleAsync()).ShouldBeTrue();
(await _page.GetByRole(AriaRole.Heading, new() { Name = "Operator Details" }).IsVisibleAsync()).ShouldBeTrue();
(await _page.GetByRole(AriaRole.Button, new() { Name = "Back to List" }).IsVisibleAsync()).ShouldBeTrue();
}, nameof(AssertOperatorViewVisibleAsync));
}

public async Task BackToOperatorListFromViewAsync()
{
await RunWithFailureArtifactsAsync(async () =>
{
await _page.GetByRole(AriaRole.Button, new() { Name = "Back to List" }).ClickAsync();
await _page.WaitForLoadStateAsync(LoadState.NetworkIdle);
}, nameof(BackToOperatorListFromViewAsync));
}

public async Task OpenOperatorEditAsync(string operatorName)
{
await RunWithFailureArtifactsAsync(async () =>
{
var editButton = GetOperatorRow(operatorName).GetByRole(AriaRole.Button, new() { Name = "Edit" });
await editButton.WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Timeout = 10000
});

await editButton.ClickAsync();
}, nameof(OpenOperatorEditAsync));
}

public async Task AssertOperatorEditVisibleAsync(string operatorName)
{
await RunWithFailureArtifactsAsync(async () =>
{
var heading = _page.GetByRole(AriaRole.Heading, new() { Name = $"Edit Operator: {operatorName}" });
await heading.WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Timeout = 10000
});

(await heading.IsVisibleAsync()).ShouldBeTrue();
(await _page.Locator("input[placeholder='Enter operator name']").IsVisibleAsync()).ShouldBeTrue();
(await _page.GetByRole(AriaRole.Button, new() { Name = "Update Operator" }).IsVisibleAsync()).ShouldBeTrue();
}, nameof(AssertOperatorEditVisibleAsync));
}

public async Task CancelOperatorEditAsync()
{
await RunWithFailureArtifactsAsync(async () =>
{
await _page.GetByRole(AriaRole.Button, new() { Name = "Cancel" }).ClickAsync();
await _page.WaitForLoadStateAsync(LoadState.NetworkIdle);
}, nameof(CancelOperatorEditAsync));
}

public async Task OpenNewOperatorScreenAsync()
{
await RunWithFailureArtifactsAsync(async () =>
{
await _page.Locator("#newOperatorButton").ClickAsync();
await _page.WaitForURLAsync(new Regex(@".*/operators/new.*", RegexOptions.IgnoreCase));
await _page.WaitForLoadStateAsync(LoadState.NetworkIdle);
}, nameof(OpenNewOperatorScreenAsync));
}

public async Task AssertNewOperatorScreenVisibleAsync()
{
await RunWithFailureArtifactsAsync(async () =>
{
(await WaitForAnyVisibleAsync(
"h1:has-text('Create New Operator')",
"input[placeholder='Enter operator name']",
"#createOperatorButton")).ShouldBeTrue();

var heading = _page.GetByRole(AriaRole.Heading, new() { Name = "Create New Operator" });
(await heading.IsVisibleAsync()).ShouldBeTrue();
(await _page.Locator("input[placeholder='Enter operator name']").IsVisibleAsync()).ShouldBeTrue();
(await _page.Locator("#createOperatorButton").IsVisibleAsync()).ShouldBeTrue();
}, nameof(AssertNewOperatorScreenVisibleAsync));
}

public async Task CreateOperatorAsync(string operatorName)
{
await RunWithFailureArtifactsAsync(async () =>
{
await _page.Locator("input[placeholder='Enter operator name']").FillAsync(operatorName);
await _page.Locator("#createOperatorButton").ClickAsync();
}, nameof(CreateOperatorAsync));
}

public async Task ClickSignInButtonAsync()
{
await RunWithFailureArtifactsAsync(async () =>
Expand Down Expand Up @@ -492,12 +663,30 @@ await _page.Locator(".animate-spin").WaitForAsync(new LocatorWaitForOptions
});
}

private async Task WaitForOperatorManagementAsync()
{
var spinner = _page.Locator(".animate-spin");
if (await spinner.CountAsync() > 0)
{
await spinner.First.WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Hidden,
Timeout = 10000
});
}
}

private ILocator GetAssignedOperatorRow(string operatorName)
{
return _page.Locator("div.flex.items-center.justify-between.p-3.bg-gray-50.rounded-lg")
.Filter(new() { HasText = operatorName });
}

private ILocator GetOperatorRow(string operatorName)
{
return _page.Locator("tbody tr").Filter(new() { HasText = operatorName });
}

private async Task<bool> IsAnyVisibleAsync(params string[] selectors)
{
foreach (var selector in selectors)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
@base @background @estate
Feature: Operator Management
As an authenticated estate user
I want to move through the operator management screens
So that I can manage operators from one end-to-end journey

Background:
Given I create the following roles
| Role Name |
| Administrator |
| Estate |

Given I create the following api scopes
| Name | DisplayName | Description |
| transactionProcessor | Transaction Processor REST Scope | Scope for Transaction Processor REST |
| fileProcessor | File Processor REST Scope | Scope for File Processor REST |
| estateReporting | Estate Reporting REST Scope | Scope for Estate Reporting REST |

Given I create the following api resources
| Name | DisplayName | Secret | Scopes | UserClaims |
| transactionProcessor | Transaction Processor REST | Secret1 | transactionProcessor | merchantId,estateId,role |
| fileProcessor | File Processor REST | Secret1 | fileProcessor | merchantId,estateId,role |
| estateReporting | Estate Reporting REST | Secret1 | estateReporting | merchantId,estateId,role |

Given I create the following identity resources
| Name | DisplayName | Description | UserClaims |
| openid | Your user identifier | | sub |
| profile | User profile | Your user profile information (first name, last name, etc.) | name,role,email,given_name,middle_name,family_name,estateId,merchantId |
| email | Email | Email and Email Verified Flags | email_verified,email |

Given I create the following clients
| ClientId | Name | Secret | Scopes | GrantTypes | RedirectUris | PostLogoutRedirectUris | RequireConsent | AllowOfflineAccess | ClientUri |
| serviceClient | Service Client | Secret1 | transactionProcessor,fileProcessor,estateReporting | client_credentials | | | | | |
| estateUIClient | Merchant Client | Secret1 | fileProcessor,transactionProcessor,estateReporting,openid,email,profile | hybrid | https://127.0.0.1:[port]/signin-oidc | https://127.0.0.1:[port]/signout-oidc | false | true | https://127.0.0.1:[port] |

Given I create the following users
| Email Address | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles | Password |
| administrator@admin.co.uk | 123456789 | Test | | User 1 | | Administrator | 123456 |

Given I have a token to access the transaction Processor resource
| ClientId |
| serviceClient |

Given I have created the following estates
| EstateName |
| Test Estate |

And I have created the following operators
| EstateName | OperatorName | RequireCustomMerchantNumber | RequireCustomTerminalNumber |
| Test Estate | Test Operator | True | True |
| Test Estate | Spare Operator | False | False |

And I have assigned the following operators to the estates
| EstateName | OperatorName |
| Test Estate | Test Operator |

And I have created the following security users
| EmailAddress | Password | GivenName | FamilyName | EstateName |
| estateuser@testestate1.co.uk | 123456 | TestEstate | User1 | Test Estate |

Given the user navigates to the entry screen
Then I should see the entry screen
When I open the estate information page
Then I should see the estate info page
And I click on the Sign In Button
Then I am presented with a login screen
When I login with the username 'estateuser@testestate1.co.uk' and password '123456'
Then I should see the dashboard heading

Scenario: Estate users can navigate the operator screens and manage operators
When I open the operator management screen
Then I should see the operator management heading
And I should see the operator 'Test Operator' in the operator list
When I open the operator view for 'Test Operator'
Then I should see the operator view page for 'Test Operator'
When I go back to the operator list from the view page
Then I should see the operator management heading
When I open the operator edit page for 'Test Operator'
Then I should see the operator edit page for 'Test Operator'
When I cancel operator editing
Then I should see the operator management heading
When I open the new operator screen
Then I should see the new operator screen
When I create the operator 'Integration Operator'
Then I should see the operator management heading
And I should see the operator 'Integration Operator' in the operator list
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using EstateManagementUI.IntegrationTests.Common;
using Microsoft.Playwright;
using Reqnroll;

namespace EstateManagementUI.IntegrationTests.Steps;

[Binding]
[Scope(Tag = "estate")]
public sealed class OperatorManagementSteps
{
private readonly IPage _page;
private readonly TestingContext _testingContext;

public OperatorManagementSteps(IPage page, TestingContext testingContext)
{
_page = page;
_testingContext = testingContext;
}

[When("I open the operator management screen")]
public Task WhenIOpenTheOperatorManagementScreen() => GetHelper().OpenOperatorManagementScreenAsync();

[Then("I should see the operator management heading")]
public Task ThenIShouldSeeTheOperatorManagementHeading() => GetHelper().AssertOperatorManagementHeadingVisibleAsync();

[Then("I should see the operator {string} in the operator list")]
public Task ThenIShouldSeeTheOperatorInTheOperatorList(string operatorName) => GetHelper().AssertOperatorListContainsAsync(operatorName);

[When("I open the operator view for {string}")]
public Task WhenIOpenTheOperatorViewFor(string operatorName) => GetHelper().OpenOperatorViewAsync(operatorName);

[Then("I should see the operator view page for {string}")]
public Task ThenIShouldSeeTheOperatorViewPageFor(string operatorName) => GetHelper().AssertOperatorViewVisibleAsync(operatorName);

[When("I go back to the operator list from the view page")]
public Task WhenIGoBackToTheOperatorListFromTheViewPage() => GetHelper().BackToOperatorListFromViewAsync();

[When("I open the operator edit page for {string}")]
public Task WhenIOpenTheOperatorEditPageFor(string operatorName) => GetHelper().OpenOperatorEditAsync(operatorName);

[Then("I should see the operator edit page for {string}")]
public Task ThenIShouldSeeTheOperatorEditPageFor(string operatorName) => GetHelper().AssertOperatorEditVisibleAsync(operatorName);

[When("I cancel operator editing")]
public Task WhenICancelOperatorEditing() => GetHelper().CancelOperatorEditAsync();

[When("I open the new operator screen")]
public Task WhenIOpenTheNewOperatorScreen() => GetHelper().OpenNewOperatorScreenAsync();

[Then("I should see the new operator screen")]
public Task ThenIShouldSeeTheNewOperatorScreen() => GetHelper().AssertNewOperatorScreenVisibleAsync();

[When("I create the operator {string}")]
public Task WhenICreateTheOperator(string operatorName) => GetHelper().CreateOperatorAsync(operatorName);

private DashboardPageHelper GetHelper() => new(_page, _testingContext);
}
Loading