Skip to content

Commit b71c5a5

Browse files
committed
Add end-to-end testing example
1 parent 5ae5176 commit b71c5a5

File tree

126 files changed

+67435
-395
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+67435
-395
lines changed

.editorconfig

Lines changed: 378 additions & 0 deletions
Large diffs are not rendered by default.

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ jobs:
88
- name: Setup .NET Core SDK
99
uses: actions/setup-dotnet@v4
1010
- name: Test with the dotnet CLI
11-
run: dotnet test
11+
run: dotnet test --filter 'FullyQualifiedName!~FunctionalTests'

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ The most important tools that are used in the automated tests are as follows:
1111
| [xUnit v3](https://xunit.net/?tabs=cs) | The testing framework. You can use any testing framework that you like though |
1212
| [NSubstitute](https://nsubstitute.github.io/) | Library for mocking. Any mocking library will work. This project doesn't do extensive mocking, but for example `IPublishedValueFallback` is a mandatory parameter for any published content item, even if you don't actually use it. It's just convenient to insert a mock. |
1313
| [Test Containers](https://testcontainers.com/) | Automatically creates docker containers while running tests. It is used to create an empty SQL Server database that is automatically cleaned up after testing. It is required to have **Docker Desktop** installed and running while running these tests. |
14+
| [Playwright](https://playwright.dev/dotnet/) | Framework for browser automation. After setting up a content scenario, playwright can open a browser and actually visit the website and make assertions based on content and behaviour in a realistic user interaction. |
1415

1516
For a more exhaustive list of tools, I recommend checking out the .csproj files in each testing project.
1617

@@ -20,6 +21,7 @@ Use this project as a reference to understand how to get started with automated
2021

2122
- [Unit testing project](./test/TestingExample.Website.UnitTests/)
2223
- [Integration testing project](./test/TestingExample.Website.IntegrationTests/)
24+
- [End-to-end / functional testing project](./test/TestingExample.Website.FunctionalTests/)
2325

2426
## Running this project locally
2527

@@ -30,3 +32,7 @@ dotnet test
3032
```
3133

3234
Alternatively, you can use your automated test explorer in your IDE.
35+
36+
| ℹ️ Note on running end-to-end tests |
37+
|---|
38+
| Running end-to-end tests is more complicated than running the unit tests and integration tests in this project. The end-to-end tests will likely fail when you run them with the dotnet test command. For detailed instructions, check out the readme in the FunctionTests test project |

TestingExample.sln

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestingExample.Website.Unit
1313
EndProject
1414
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestingExample.Website.IntegrationTests", "test\TestingExample.Website.IntegrationTests\TestingExample.Website.IntegrationTests.csproj", "{0B6A2F7A-D866-4149-861F-21BCCB283B61}"
1515
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestingExample.ManagementApiClient", "test\TestingExample.ManagementApiClient\TestingExample.ManagementApiClient.csproj", "{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}"
17+
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestingExample.ManagementApiClient.UnitTests", "test\TestingExample.ManagementApiClient.UnitTests\TestingExample.ManagementApiClient.UnitTests.csproj", "{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}"
19+
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestingExample.Website.FunctionalTests", "test\TestingExample.Website.FunctionalTests\TestingExample.Website.FunctionalTests.csproj", "{A5239CC5-F6E6-4083-BCD2-14CE45E23519}"
21+
EndProject
1622
Global
1723
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1824
Debug|Any CPU = Debug|Any CPU
@@ -59,6 +65,42 @@ Global
5965
{0B6A2F7A-D866-4149-861F-21BCCB283B61}.Release|x64.Build.0 = Release|Any CPU
6066
{0B6A2F7A-D866-4149-861F-21BCCB283B61}.Release|x86.ActiveCfg = Release|Any CPU
6167
{0B6A2F7A-D866-4149-861F-21BCCB283B61}.Release|x86.Build.0 = Release|Any CPU
68+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
69+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
70+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Debug|x64.ActiveCfg = Debug|Any CPU
71+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Debug|x64.Build.0 = Debug|Any CPU
72+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Debug|x86.ActiveCfg = Debug|Any CPU
73+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Debug|x86.Build.0 = Debug|Any CPU
74+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
75+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Release|Any CPU.Build.0 = Release|Any CPU
76+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Release|x64.ActiveCfg = Release|Any CPU
77+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Release|x64.Build.0 = Release|Any CPU
78+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Release|x86.ActiveCfg = Release|Any CPU
79+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA}.Release|x86.Build.0 = Release|Any CPU
80+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
81+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
82+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Debug|x64.ActiveCfg = Debug|Any CPU
83+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Debug|x64.Build.0 = Debug|Any CPU
84+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Debug|x86.ActiveCfg = Debug|Any CPU
85+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Debug|x86.Build.0 = Debug|Any CPU
86+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
87+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Release|Any CPU.Build.0 = Release|Any CPU
88+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Release|x64.ActiveCfg = Release|Any CPU
89+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Release|x64.Build.0 = Release|Any CPU
90+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Release|x86.ActiveCfg = Release|Any CPU
91+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1}.Release|x86.Build.0 = Release|Any CPU
92+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
93+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Debug|Any CPU.Build.0 = Debug|Any CPU
94+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Debug|x64.ActiveCfg = Debug|Any CPU
95+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Debug|x64.Build.0 = Debug|Any CPU
96+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Debug|x86.ActiveCfg = Debug|Any CPU
97+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Debug|x86.Build.0 = Debug|Any CPU
98+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Release|Any CPU.ActiveCfg = Release|Any CPU
99+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Release|Any CPU.Build.0 = Release|Any CPU
100+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Release|x64.ActiveCfg = Release|Any CPU
101+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Release|x64.Build.0 = Release|Any CPU
102+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Release|x86.ActiveCfg = Release|Any CPU
103+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519}.Release|x86.Build.0 = Release|Any CPU
62104
EndGlobalSection
63105
GlobalSection(SolutionProperties) = preSolution
64106
HideSolutionNode = FALSE
@@ -67,5 +109,8 @@ Global
67109
{07BCB56F-FFB1-482B-BCC2-928C98D1DE4E} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
68110
{5BAE0075-20C7-4CDA-B45D-C309FB036BB3} = {0C88DD14-F956-CE84-757C-A364CCF449FC}
69111
{0B6A2F7A-D866-4149-861F-21BCCB283B61} = {0C88DD14-F956-CE84-757C-A364CCF449FC}
112+
{D663DE65-A3B9-4A48-8CDF-F0BF083A44DA} = {0C88DD14-F956-CE84-757C-A364CCF449FC}
113+
{3C5159A5-219F-4B61-B9E8-2B9A249AA6E1} = {0C88DD14-F956-CE84-757C-A364CCF449FC}
114+
{A5239CC5-F6E6-4083-BCD2-14CE45E23519} = {0C88DD14-F956-CE84-757C-A364CCF449FC}
70115
EndGlobalSection
71116
EndGlobal
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Umbraco.Cms.Core.Composing;
2+
using Umbraco.Cms.Core.Models.PublishedContent;
3+
4+
namespace TestingExample.Website.Home;
5+
6+
[HideFromTypeFinder]
7+
public class ExtendedHomepage(
8+
Umbraco.Cms.Web.Common.PublishedModels.Homepage content,
9+
IPublishedValueFallback publishedValueFallback,
10+
List<HomepageCard> cards)
11+
: Umbraco.Cms.Web.Common.PublishedModels.Homepage(content, publishedValueFallback)
12+
{
13+
public List<HomepageCard> Cards { get; } = cards;
14+
}
15+
16+
public record HomepageCard(int Id, string Title, string Introduction);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Umbraco.Cms.Core.Composing;
2+
3+
namespace TestingExample.Website.Home;
4+
5+
public class HomepageComposer : IComposer
6+
{
7+
public void Compose(IUmbracoBuilder builder)
8+
{
9+
builder.Services.AddScoped<HomepageRequestHandler>();
10+
}
11+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.AspNetCore.Mvc.ViewEngines;
3+
4+
using Umbraco.Cms.Core.Web;
5+
using Umbraco.Cms.Web.Common.Controllers;
6+
using Umbraco.Cms.Web.Common.PublishedModels;
7+
8+
namespace TestingExample.Website.Home;
9+
10+
public class HomepageController(
11+
ILogger<RenderController> logger,
12+
ICompositeViewEngine compositeViewEngine,
13+
IUmbracoContextAccessor umbracoContextAccessor,
14+
HomepageRequestHandler requestHandler)
15+
: RenderController(logger, compositeViewEngine, umbracoContextAccessor)
16+
{
17+
private readonly HomepageRequestHandler _requestHandler = requestHandler;
18+
19+
public IActionResult Homepage()
20+
{
21+
return CurrentPage is Homepage Homepage
22+
? CurrentTemplate(_requestHandler.CreateHomepageViewModel(Homepage))
23+
: NotFound();
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
using TestingExample.Website.PublishedContent;
2+
23
using Umbraco.Cms.Core.Models.PublishedContent;
34
using Umbraco.Cms.Web.Common.PublishedModels;
45

5-
namespace TestingExample.Website.Homepage;
6+
namespace TestingExample.Website.Home;
67

7-
public class ExampleWebsiteRequestHandler(
8+
public class HomepageRequestHandler(
89
IPublishedValueFallback publishedValueFallback,
910
IPublishedContentOperations publishedContentOperations)
1011
{
1112
private readonly IPublishedValueFallback _publishedValueFallback = publishedValueFallback;
1213
private readonly IPublishedContentOperations _publishedContentOperations = publishedContentOperations;
1314

14-
public ExtendedExampleWebsite CreateHomepageViewModel(ExampleWebsite exampleWebsite)
15+
public ExtendedHomepage CreateHomepageViewModel(Homepage Homepage)
1516
{
16-
return new ExtendedExampleWebsite(
17-
exampleWebsite,
17+
return new ExtendedHomepage(
18+
Homepage,
1819
_publishedValueFallback,
19-
[.. exampleWebsite.Children<ExampleSubpage>(_publishedContentOperations)
20-
.Select(subPage => new ExampleCard(
20+
[.. Homepage.Children<ContentPage>(_publishedContentOperations)
21+
.Select(subPage => new HomepageCard(
2122
subPage.Id,
2223
subPage.Name,
23-
subPage.Introduction ?? string.Empty))]);
24+
string.Empty))]);
2425
}
2526
}

src/TestingExample.Website/Homepage/ExampleWebsiteComposer.cs

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/TestingExample.Website/Homepage/ExampleWebsiteController.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)