Skip to content

Commit

Permalink
playwright playground
Browse files Browse the repository at this point in the history
  • Loading branch information
fakefeik committed Jan 17, 2024
1 parent 6b3d502 commit 882afa9
Show file tree
Hide file tree
Showing 32 changed files with 1,109 additions and 251 deletions.
12 changes: 11 additions & 1 deletion .github/workflows/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ jobs:
- name: Pack dotnet
run: dotnet pack --no-build --configuration Release ./DbViewer.sln

- name: Install Playwright
run: pwsh ./DbViewer.Tests/bin/Release/net6.0/playwright.ps1 install chromium --with-deps

- name: Build front
run: yarn --cwd db-viewer-ui build

- name: Check C# code style
run: dotnet jb cleanupcode DbViewer.sln --profile=CatalogueCleanup --exclude=./DbViewer.TestApi/Migrations/*.cs --verbosity=WARN && git diff --exit-code -- ':!./db-viewer-ui/.yarn'
run: dotnet jb cleanupcode DbViewer.sln --profile=CatalogueCleanup --exclude=./DbViewer.TestApi/Migrations/*.cs --verbosity=WARN --no-build && git diff --exit-code -- ':!./db-viewer-ui/.yarn'

- name: Check front code
run: yarn --cwd db-viewer-ui lint
Expand Down Expand Up @@ -79,6 +82,13 @@ jobs:
**/*.nupkg
**/*.tgz
if-no-files-found: error

- name: Upload Playwright traces
if: failure()
uses: actions/upload-artifact@v3
with:
name: traces
path: DbViewer.Tests/bin/Release/net6.0/out/*.zip

publish:
runs-on: ubuntu-20.04
Expand Down
1 change: 1 addition & 0 deletions DbViewer.Tests/DbViewer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.2.0" />
<PackageReference Include="Kontur.ReactUI.SeleniumTesting" Version="3.6.0" />
<PackageReference Include="Microsoft.Playwright" Version="1.40.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
Expand Down
60 changes: 60 additions & 0 deletions DbViewer.Tests/FrontTests/AutoFill/PwAutoFill.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Linq;
using System.Reflection;

using Microsoft.Playwright;

using SkbKontur.DbViewer.Tests.FrontTests.Playwright;

namespace SkbKontur.DbViewer.Tests.FrontTests.AutoFill
{
public class PwAutoFill
{
public static TPage InitializePage<TPage>(IPage page)
where TPage : PwPageBase
{
var newPage = (TPage)Activator.CreateInstance(typeof(TPage), page)!;
InitializeControls(newPage, newPage.Page, null);
return newPage;
}

public static void InitializeControls(object instance, IPage page, ILocator? parent)
{
var properties = instance
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.CanWrite && typeof(PwControlBase).IsAssignableFrom(p.PropertyType) && !p.GetCustomAttributes<SkipAutoFillAttribute>().Any());

foreach (var property in properties)
{
var locator = LocatorForProperty(property, page, parent);
var value = Activator.CreateInstance(property.PropertyType, locator)!;
InitializeControls(value, page, locator);
property.SetValue(instance, value);
}
}

public static ILocator LocatorForProperty(PropertyInfo property, IPage page, ILocator? parent)
{
var selector = property
.GetCustomAttributes<SelectorAttribute>()
.Select(x => x.Selector.ToString())
.FirstOrDefault();

if (string.IsNullOrEmpty(selector))
return GetByTestId(page, parent, property.Name);

var selectors = selector.Split(" ", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
return selectors
.Aggregate(
parent,
(current, s) => s.StartsWith("##")
? GetByTestId(page, current, s[2..])
: GetLocator(page, current, s)
)!;
}

private static ILocator GetByTestId(IPage page, ILocator? parent, string tid) => parent == null ? page.GetByTestId(tid) : parent.GetByTestId(tid);
private static ILocator GetLocator(IPage page, ILocator? parent, string loc) => parent == null ? page.Locator(loc) : parent.Locator(loc);
}
}
334 changes: 164 additions & 170 deletions DbViewer.Tests/FrontTests/BusinessObjectsChangeValuesTest.cs

Large diffs are not rendered by default.

78 changes: 42 additions & 36 deletions DbViewer.Tests/FrontTests/BusinessObjectsDeleteTest.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using NUnit.Framework;

using SkbKontur.DbViewer.TestApi.EntityFramework;
using SkbKontur.DbViewer.Tests.FrontTests.Helpers;
using SkbKontur.DbViewer.Tests.FrontTests.Pages;
using SkbKontur.DbViewer.Tests.FrontTests.Playwright;

using ConfirmDeleteObjectModal = SkbKontur.DbViewer.Tests.FrontTests.Controls.ConfirmDeleteObjectModal;

namespace SkbKontur.DbViewer.Tests.FrontTests
{
Expand All @@ -16,20 +19,20 @@ public class BusinessObjectsDeleteTest
/// Проверяем, что кнопка с удалением бизнес объекта на странице с таблицей доступна только с SuperUserAccessLevel.God
/// </summary>
[Test]
public void DeleteViaSearchPageRequiresGodAccess()
public async Task DeleteViaSearchPageRequiresGodAccess()
{
var ftpUser = CreateFtpUser();

using var browser = new BrowserForTests();
var businessObjectPage = browser.SwitchTo<BusinessObjectTablePage>("FtpUser");
await using var browser = new Browser();
var businessObjectPage = await browser.SwitchTo<PwBusinessObjectTablePage>("FtpUser");

businessObjectPage.OpenFilter.Click();
businessObjectPage.FilterModal.GetFilter("Login").Input.ClearAndInputText(ftpUser.Login);
businessObjectPage.FilterModal.Apply.Click();
await businessObjectPage.OpenFilter.Click();
var filter = await businessObjectPage.FilterModal.GetFilter("Login");
await filter.Input.ClearAndInputText(ftpUser.Login);
await businessObjectPage.FilterModal.Apply.Click();

businessObjectPage = browser.RefreshUntil(businessObjectPage, x => x.BusinessObjectItems.IsPresent.Get());
businessObjectPage.BusinessObjectItems.WaitCount(1);
businessObjectPage.BusinessObjectItems[0].Delete.IsPresent.Wait().That(Is.False, "Delete link should only be present for gods");
await businessObjectPage.BusinessObjectItems.WaitCount(1);
await businessObjectPage.BusinessObjectItems[0].Delete.WaitAbsence();
}

/// <summary>
Expand All @@ -39,24 +42,26 @@ public void DeleteViaSearchPageRequiresGodAccess()
/// <param name="confirmDeletion"></param>
[TestCase(true)]
[TestCase(false)]
public void DeleteViaSearchPage(bool confirmDeletion)
public async Task DeleteViaSearchPage(bool confirmDeletion)
{
var ftpUser = CreateFtpUser();

using var browser = new BrowserForTests();
var businessObjectPage = browser.LoginAsSuperUser().SwitchTo<BusinessObjectTablePage>("FtpUser");
await using var browser = new Browser();

var adminBrowser = await browser.LoginAsSuperUser();
var businessObjectPage = await adminBrowser.SwitchTo<PwBusinessObjectTablePage>("FtpUser");

businessObjectPage.OpenFilter.Click();
businessObjectPage.FilterModal.GetFilter("Login").Input.ClearAndInputText(ftpUser.Login);
businessObjectPage.FilterModal.Apply.Click();
await businessObjectPage.OpenFilter.Click();
var filter = await businessObjectPage.FilterModal.GetFilter("Login");
await filter.Input.ClearAndInputText(ftpUser.Login);
await businessObjectPage.FilterModal.Apply.Click();

businessObjectPage = browser.RefreshUntil(businessObjectPage, x => x.BusinessObjectItems.IsPresent.Get());
businessObjectPage.BusinessObjectItems.WaitCount(1);
businessObjectPage.BusinessObjectItems[0].Delete.Click();
ConfirmDeletion(businessObjectPage.ConfirmDeleteObjectModal, confirmDeletion);
await businessObjectPage.BusinessObjectItems.WaitCount(1);
await businessObjectPage.BusinessObjectItems[0].Delete.Click();
await ConfirmDeletion(businessObjectPage.ConfirmDeleteObjectModal, confirmDeletion);

if (confirmDeletion)
businessObjectPage.BusinessObjectItems.WaitAbsence();
await businessObjectPage.BusinessObjectItems.WaitAbsence();

AssertFtpUserExistence(ftpUser.Id, confirmDeletion);
}
Expand All @@ -65,13 +70,13 @@ public void DeleteViaSearchPage(bool confirmDeletion)
/// Проверяем, что кнопка с удалением бизнес объекта на странице с конкретным объектом доступна только с SuperUserAccessLevel.God
/// </summary>
[Test]
public void DeleteViaDetailsPageRequiresGodAccess()
public async Task DeleteViaDetailsPageRequiresGodAccess()
{
var ftpUser = CreateFtpUser();

using var browser = new BrowserForTests();
var detailsPage = browser.SwitchTo<BusinessObjectDetailsPage>("FtpUser", $"Id={ftpUser.Id}");
detailsPage.Delete.IsPresent.Wait().That(Is.False, "Delete link should only be present for gods");
await using var browser = new Browser();
var detailsPage = await browser.SwitchTo<PwBusinessObjectDetailsPage>("FtpUser", $"Id={ftpUser.Id}");
await detailsPage.Delete.WaitAbsence();
}

/// <summary>
Expand All @@ -81,27 +86,28 @@ public void DeleteViaDetailsPageRequiresGodAccess()
/// <param name="confirmDeletion"></param>
[TestCase(true)]
[TestCase(false)]
public void DeleteViaDetailsPage(bool confirmDeletion)
public async Task DeleteViaDetailsPage(bool confirmDeletion)
{
var ftpUser = CreateFtpUser();

using var browser = new BrowserForTests();
var detailsPage = browser.LoginAsSuperUser().SwitchTo<BusinessObjectDetailsPage>("FtpUser", $"Id={ftpUser.Id}");
detailsPage.Delete.Click();
ConfirmDeletion(detailsPage.ConfirmDeleteObjectModal, confirmDeletion);
await using var browser = new Browser();

var adminBrowser = await browser.LoginAsSuperUser();
var detailsPage = await adminBrowser.SwitchTo<PwBusinessObjectDetailsPage>("FtpUser", $"Id={ftpUser.Id}");
await detailsPage.Delete.Click();
await ConfirmDeletion(detailsPage.ConfirmDeleteObjectModal, confirmDeletion);

if (confirmDeletion)
detailsPage.GoTo<BusinessObjectTablePage>();
detailsPage.GoTo<PwBusinessObjectTablePage>();

AssertFtpUserExistence(ftpUser.Id, confirmDeletion);
}

private static void ConfirmDeletion(ConfirmDeleteObjectModal modal, bool confirmDeletion)
private static Task ConfirmDeletion(ConfirmDeleteObjectModal modal, bool confirmDeletion)
{
if (confirmDeletion)
modal.Delete.Click();
else
modal.Cancel.Click();
return confirmDeletion
? modal.Delete.Click()
: modal.Cancel.Click();
}

private static void AssertFtpUserExistence(Guid userId, bool deletionConfirmed)
Expand Down
91 changes: 48 additions & 43 deletions DbViewer.Tests/FrontTests/BusinessObjectsSearchTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using NUnit.Framework;
using System.Threading.Tasks;

using NUnit.Framework;

using SkbKontur.DbViewer.Tests.FrontTests.Helpers;
using SkbKontur.DbViewer.Tests.FrontTests.Pages;
using SkbKontur.DbViewer.Tests.FrontTests.Playwright;

namespace SkbKontur.DbViewer.Tests.FrontTests
{
Expand All @@ -12,77 +14,80 @@ public class BusinessObjectsSearchTests
/// Вводим в строке поиска запрос в R# стиле, проверяем ожидаемое количество и название каждого объекта
/// </summary>
[Test]
public void TestSearch()
public async Task TestSearch()
{
using var browser = new BrowserForTests();
await using var browser = new Browser();

var businessObjectsPage = await browser.SwitchTo<PwBusinessObjectsPage>();
await businessObjectsPage.FilterInput.ClearAndInputText("CI");
await businessObjectsPage.ObjectGroups.WaitCount(1);

var pagedObjects = await businessObjectsPage.ObjectGroups.GetItemWithText(x => x.Name, "CQL Paged Objects");
await pagedObjects.ObjectsList
.Select(x => x.ObjectLink)
.WaitText("CqlOrganizationInfo", "CqlUserInfo");

await businessObjectsPage.FilterInput.ClearAndInputText("DocMe");
await businessObjectsPage.ObjectGroups.WaitCount(2);

var businessObjectsPage = browser.SwitchTo<BusinessObjectsPage>();
businessObjectsPage.FilterInput.ClearAndInputText("CI");
businessObjectsPage.ObjectGroups.WaitCount(1);
businessObjectsPage.ObjectGroups
.GetItemWithText(x => x.Name.Text, "CQL Paged Objects").ObjectsList
.Wait(x => x.ObjectLink.Text)
.That(Is.EqualTo(new[] {"CqlOrganizationInfo", "CqlUserInfo"}));
var cqlObjects = await businessObjectsPage.ObjectGroups.GetItemWithText(x => x.Name, "CQL Objects");
await cqlObjects.ObjectsList
.Select(x => x.ObjectLink)
.WaitText(new[] {"DocumentBindingsMeta"});

businessObjectsPage.FilterInput.ClearAndInputText("DocMe");
businessObjectsPage.ObjectGroups.WaitCount(2);
businessObjectsPage.ObjectGroups
.GetItemWithText(x => x.Name.Text, "CQL Objects").ObjectsList
.Wait(x => x.ObjectLink.Text)
.That(Is.EqualTo(new[] {"DocumentBindingsMeta"}));
businessObjectsPage.ObjectGroups
.GetItemWithText(x => x.Name.Text, "CQL Paged Objects").ObjectsList
.Wait(x => x.ObjectLink.Text)
.That(Is.EqualTo(new[] {"CqlDocumentMeta"}));
var cqlPagedObjects = await businessObjectsPage.ObjectGroups.GetItemWithText(x => x.Name, "CQL Paged Objects");
await cqlPagedObjects.ObjectsList
.Select(x => x.ObjectLink)
.WaitText(new[] {"CqlDocumentMeta"});
}

/// <summary>
/// Проверяем, что у таблицы FtpUser есть плашка indexed
/// </summary>
[Test]
public void TestSearchIndexedField()
public async Task TestSearchIndexedField()
{
using var browser = new BrowserForTests();
await using var browser = new Browser();

var businessObjects = browser.SwitchTo<BusinessObjectsPage>();
businessObjects.FilterInput.ClearAndInputText("UsersTable");
businessObjects.ObjectGroups.WaitCount(1);
var businessObjects = await browser.SwitchTo<PwBusinessObjectsPage>();
await businessObjects.FilterInput.ClearAndInputText("UsersTable");
await businessObjects.ObjectGroups.WaitCount(1);

var objects = businessObjects.ObjectGroups.GetItemWithText(x => x.Name.Text, "Postgres Objects");
objects.IndexedLabel.WaitPresence();
objects.ObjectsList.WaitCount(1);
objects.ObjectsList[0].ObjectLink.WaitText("UsersTable");
var objects = await businessObjects.ObjectGroups.GetItemWithText(x => x.Name, "Postgres Objects");
await objects.IndexedLabel.WaitPresence();
await objects.ObjectsList.WaitCount(1);
await objects.ObjectsList[0].ObjectLink.WaitText("UsersTable");
}

/// <summary>
/// Вводим в поиск CqlDocumentPrintingInfo.
/// Проверяем что нам выдает ровно одну ссылку
/// </summary>
[Test]
public void TestSearchLink()
public async Task TestSearchLink()
{
using var browser = new BrowserForTests();
await using var browser = new Browser();

var businessObjectsPage = browser.SwitchTo<BusinessObjectsPage>();
businessObjectsPage.FilterInput.ClearAndInputText("DocumentPrintingInfo");
businessObjectsPage.ObjectGroups.WaitCount(1);
businessObjectsPage.ObjectGroups[0].ObjectsList.WaitCount(1);
businessObjectsPage.FindBusinessObjectLink("CQL Objects", "DocumentPrintingInfo").WaitPresence();
var businessObjectsPage = await browser.SwitchTo<PwBusinessObjectsPage>();
await businessObjectsPage.FilterInput.ClearAndInputText("DocumentPrintingInfo");
await businessObjectsPage.ObjectGroups.WaitCount(1);
await businessObjectsPage.ObjectGroups[0].ObjectsList.WaitCount(1);
await businessObjectsPage.FindBusinessObjectLink("CQL Objects", "DocumentPrintingInfo");
}

/// <summary>
/// Переходим по ссылке на CqlDocumentPrintingInfo
/// Проверяем, что ссылка ведет туда, куда нам нужно
/// </summary>
[Test]
public void TestLinkShouldReferToShowTablePage()
public async Task TestLinkShouldReferToShowTablePage()
{
using var browser = new BrowserForTests();
await using var browser = new Browser();

var businessObjectsPage = browser.SwitchTo<BusinessObjectsPage>();
var link = businessObjectsPage.FindBusinessObjectLink("CQL Objects", "DocumentPrintingInfo");
var page = link.ClickAndGoTo<BusinessObjectTablePage>();
page.Header.WaitText("DocumentPrintingInfo");
var businessObjectsPage = await browser.SwitchTo<PwBusinessObjectsPage>();
var link = await businessObjectsPage.FindBusinessObjectLink("CQL Objects", "DocumentPrintingInfo");
var page = await link.ClickAndGoTo<PwBusinessObjectTablePage>();
await page.Header.WaitText("DocumentPrintingInfo");
}
}
}
Loading

0 comments on commit 882afa9

Please sign in to comment.