Skip to content

Commit af4005d

Browse files
committed
Separated selenium and playwright versions of the frameowrk. Added basic folders and browser launch methods to the playwright one
1 parent c510852 commit af4005d

File tree

144 files changed

+479
-119
lines changed

Some content is hidden

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

144 files changed

+479
-119
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Behavioral.Automation.Playwright", "Behavioral.Automation.Playwright\Behavioral.Automation.Playwright.csproj", "{99F7423D-E6FB-48D7-9C7F-D8402D7DD4E1}"
4+
EndProject
5+
Global
6+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7+
Debug|Any CPU = Debug|Any CPU
8+
Release|Any CPU = Release|Any CPU
9+
EndGlobalSection
10+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
11+
{99F7423D-E6FB-48D7-9C7F-D8402D7DD4E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12+
{99F7423D-E6FB-48D7-9C7F-D8402D7DD4E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
13+
{99F7423D-E6FB-48D7-9C7F-D8402D7DD4E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
14+
{99F7423D-E6FB-48D7-9C7F-D8402D7DD4E1}.Release|Any CPU.Build.0 = Release|Any CPU
15+
EndGlobalSection
16+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
12+
<PackageReference Include="Microsoft.Playwright" Version="1.22.0" />
13+
<PackageReference Include="NUnit" Version="3.13.2" />
14+
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
15+
<PackageReference Include="coverlet.collector" Version="3.1.0" />
16+
<PackageReference Include="SpecFlow" Version="3.9.74" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<Folder Include="Bindings" />
21+
<Folder Include="ElementStorage" />
22+
</ItemGroup>
23+
24+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System.Threading.Tasks;
2+
using Microsoft.Playwright;
3+
4+
namespace Behavioral.Automation.Playwright.Services;
5+
6+
public class BrowserRunner
7+
{
8+
public async Task<IPage> LaunchBrowser()
9+
{
10+
using var playwright = await Microsoft.Playwright.Playwright.CreateAsync();
11+
await using var browser = await playwright.Chromium.LaunchAsync();
12+
var page = await browser.NewPageAsync();
13+
await page.GotoAsync("https://playwright.dev/dotnet");
14+
15+
return page;
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using NUnit.Framework;
6+
using TechTalk.SpecFlow;
7+
using TechTalk.SpecFlow.Bindings;
8+
9+
namespace Behavioral.Automation.Playwright.Services
10+
{
11+
/// <summary>
12+
/// Contains BuildAction methods which should be used to call other bindings inside the code (multiple clicks, value selections, etc.)
13+
/// This way actions in called bindings will appear in test logs
14+
/// </summary>
15+
public class ComplexBindingBuilder : IComplexBindingBuilder
16+
{
17+
private readonly ITestRunner _runner;
18+
private readonly IStepParametersProcessor _stepParametersProcessor;
19+
private readonly ScenarioContext _scenarioContext;
20+
21+
private readonly int _indentationSize = 4;
22+
private int _indendationLevel = 1;
23+
24+
public ComplexBindingBuilder(
25+
ITestRunner runner,
26+
IStepParametersProcessor stepParametersProcessor,
27+
ScenarioContext scenarioContext)
28+
{
29+
_runner = runner;
30+
_stepParametersProcessor = stepParametersProcessor;
31+
_scenarioContext = scenarioContext;
32+
}
33+
34+
public void Indent()
35+
{
36+
_indendationLevel++;
37+
}
38+
39+
public void ReduceIndentation()
40+
{
41+
_indendationLevel--;
42+
}
43+
44+
public void BuildAction(Action method)
45+
{
46+
var attributes = method.GetMethodInfo().GetCustomAttributes<StepDefinitionBaseAttribute>(true);
47+
BuildAction(Array.Empty<object>(), attributes, method.GetMethodInfo().Name);
48+
}
49+
50+
public void BuildAction<T>(Action<T> method, params object[] pars)
51+
{
52+
var attributes = method.GetMethodInfo().GetCustomAttributes<StepDefinitionBaseAttribute>(true);
53+
BuildAction(pars, attributes, method.GetMethodInfo().Name);
54+
}
55+
56+
public void BuildAction<T1, T2>(Action<T1, T2> method, params object[] pars)
57+
{
58+
var attributes = method.GetMethodInfo().GetCustomAttributes<StepDefinitionBaseAttribute>(true);
59+
BuildAction(pars, attributes, method.GetMethodInfo().Name);
60+
}
61+
62+
public void BuildAction<T1, T2, T3>(Action<T1, T2, T3> method, params object[] pars)
63+
{
64+
var attributes = method.GetMethodInfo().GetCustomAttributes<StepDefinitionBaseAttribute>(true);
65+
BuildAction(pars, attributes, method.GetMethodInfo().Name);
66+
}
67+
68+
public void BuildAction<T1, T2, T3, T4>(Action<T1, T2, T3, T4> method, params object[] pars)
69+
{
70+
var attributes = method.GetMethodInfo().GetCustomAttributes<StepDefinitionBaseAttribute>(true);
71+
BuildAction(pars, attributes, method.GetMethodInfo().Name);
72+
}
73+
74+
private void BuildAction(object[] pars, IEnumerable<StepDefinitionBaseAttribute> attributes, string methodName)
75+
{
76+
var definitionType = GetStepDefinitionType().ToString();
77+
var attributeForCurrentStep = attributes.FirstOrDefault(a => a.GetType().Name.StartsWith(definitionType, StringComparison.OrdinalIgnoreCase));
78+
if (attributeForCurrentStep == null)
79+
{
80+
Assert.Inconclusive($"{definitionType} attribute is not provided for {methodName} binding method");
81+
}
82+
83+
string stepExpression;
84+
var table = pars.FirstOrDefault(d => d.GetType() == typeof(Table));
85+
if (table != null)
86+
{
87+
var parsList = pars.ToList();
88+
parsList.Remove(table);
89+
stepExpression = _stepParametersProcessor.CreateStepExpression(attributeForCurrentStep.Regex, parsList.ToArray());
90+
RunAction(stepExpression, (Table)table);
91+
}
92+
else
93+
{
94+
stepExpression = _stepParametersProcessor.CreateStepExpression(attributeForCurrentStep.Regex, pars);
95+
RunAction(stepExpression);
96+
}
97+
TestContext.WriteLine($"{new String(' ', _indendationLevel * _indentationSize)}{stepExpression}");
98+
}
99+
100+
private void RunAction(string stepExpression)
101+
{
102+
switch (GetStepDefinitionType())
103+
{
104+
case StepDefinitionType.Given:
105+
_runner.Given(stepExpression);
106+
break;
107+
108+
case StepDefinitionType.When:
109+
_runner.When(stepExpression);
110+
break;
111+
112+
case StepDefinitionType.Then:
113+
_runner.Then(stepExpression);
114+
break;
115+
}
116+
}
117+
118+
private StepDefinitionType GetStepDefinitionType()
119+
{
120+
try
121+
{
122+
return _scenarioContext.StepContext.StepInfo.StepDefinitionType;
123+
}
124+
catch (NullReferenceException)
125+
{
126+
return StepDefinitionType.Given;
127+
}
128+
}
129+
130+
private void RunAction(string stepExpression, Table table)
131+
{
132+
switch (_scenarioContext.StepContext.StepInfo.StepDefinitionType)
133+
{
134+
case StepDefinitionType.Given:
135+
_runner.Given(stepExpression, null, table);
136+
break;
137+
138+
case StepDefinitionType.When:
139+
_runner.When(stepExpression, null, table);
140+
break;
141+
142+
case StepDefinitionType.Then:
143+
_runner.Then(stepExpression, null, table);
144+
break;
145+
}
146+
}
147+
}
148+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
3+
namespace Behavioral.Automation.Playwright.Services
4+
{
5+
/// <summary>
6+
/// This interface contain methods which are used to call other bindings inside test methods, so their actions appear in the logs
7+
/// </summary>
8+
public interface IComplexBindingBuilder
9+
{
10+
void BuildAction(Action method);
11+
void BuildAction<T>(Action<T> method, params object[] pars);
12+
void BuildAction<T1, T2, T3, T4>(Action<T1, T2, T3, T4> method, params object[] pars);
13+
void BuildAction<T1, T2, T3>(Action<T1, T2, T3> method, params object[] pars);
14+
void BuildAction<T1, T2>(Action<T1, T2> method, params object[] pars);
15+
void Indent();
16+
void ReduceIndentation();
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Text.RegularExpressions;
2+
3+
namespace Behavioral.Automation.Playwright.Services
4+
{
5+
/// <summary>
6+
/// This interface contains methods which convert objects passed to ComplexBindingBuilder into step expression to use in Runner
7+
/// </summary>
8+
public interface IStepParametersProcessor
9+
{
10+
string CreateStepExpression(string pattern, params object[] arguments);
11+
public bool TryParseStepExpressionArguments(Regex regex, string stepExpression, out string[] arguments);
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System;
2+
using System.Text.RegularExpressions;
3+
4+
namespace Behavioral.Automation.Playwright.Services
5+
{
6+
/// <summary>
7+
/// Perform various operations with strings
8+
/// </summary>
9+
public static class StringExtensions
10+
{
11+
/// <summary>
12+
/// Parse values from the string in the given format
13+
/// </summary>
14+
/// <param name="data">String to parse</param>
15+
/// <param name="format">Parsing format</param>
16+
/// <returns>Parsed values</returns>
17+
public static string[] ParseExact(
18+
this string data,
19+
string format)
20+
{
21+
return ParseExact(data, format, false);
22+
}
23+
24+
/// <summary>
25+
/// Parse values from the string in the given format with option to ignore case
26+
/// </summary>
27+
/// <param name="data">String to parse</param>
28+
/// <param name="format">Parsing format</param>
29+
/// <param name="ignoreCase">Ignore case option</param>
30+
/// <returns>Parsed values</returns>
31+
public static string[] ParseExact(
32+
this string data,
33+
string format,
34+
bool ignoreCase)
35+
{
36+
string[] values;
37+
38+
if (TryParseExact(data, format, out values, ignoreCase))
39+
return values;
40+
else
41+
throw new ArgumentException("Format not compatible with value.");
42+
}
43+
44+
/// <summary>
45+
/// Try to parse values from string
46+
/// </summary>
47+
/// <param name="data">String to be parsed</param>
48+
/// <param name="format">Parsing format</param>
49+
/// <param name="values">Values to store parsed data</param>
50+
/// <returns>True if parsing was successful or false otherwise</returns>
51+
public static bool TryExtract(
52+
this string data,
53+
string format,
54+
out string[] values)
55+
{
56+
return TryParseExact(data, format, out values, false);
57+
}
58+
59+
/// <summary>
60+
/// Try to parse values from string with option to ignore case
61+
/// </summary>
62+
/// <param name="data">String to be parsed</param>
63+
/// <param name="format">Parsing format</param>
64+
/// <param name="values">Values to store parsed data</param>
65+
/// <param name="ignoreCase">Ignore case option</param>
66+
/// <returns>True if parsing was successful or false otherwise</returns>
67+
public static bool TryParseExact(
68+
this string data,
69+
string format,
70+
out string[] values,
71+
bool ignoreCase)
72+
{
73+
int tokenCount = 0;
74+
format = Regex.Escape(format).Replace("\\{", "{");
75+
76+
for (tokenCount = 0; ; tokenCount++)
77+
{
78+
string token = string.Format("{{{0}}}", tokenCount);
79+
if (!format.Contains(token)) break;
80+
format = format.Replace(token,
81+
string.Format("(?'group{0}'.*)", tokenCount));
82+
}
83+
84+
RegexOptions options =
85+
ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
86+
87+
Match match = new Regex(format, options).Match(data);
88+
89+
if (tokenCount != (match.Groups.Count - 1))
90+
{
91+
values = new string[] { };
92+
return false;
93+
}
94+
else
95+
{
96+
values = new string[tokenCount];
97+
for (int index = 0; index < tokenCount; index++)
98+
values[index] =
99+
match.Groups[string.Format("group{0}", index)].Value;
100+
return true;
101+
}
102+
}
103+
104+
/// <summary>
105+
/// Convert string into int
106+
/// </summary>
107+
/// <param name="s">String to get number from</param>
108+
/// <returns>int converted from string</returns>
109+
public static int ParseNumberFromString(string s)
110+
{
111+
return int.Parse(Regex.Match(s, @"\d+").Value);
112+
}
113+
}
114+
}

CHANGELOG.md renamed to Behavioral.Automation.Playwright/CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
[1.11.0] - 2022-05-20
7+
[0.1] - 2022-06-03
88
### Added
9-
- Added Assert.ShouldBecome() with ability to pass error message as a function
9+
- Added basic folder structure and browser launch methods for Playwright framework
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TBD

0 commit comments

Comments
 (0)