├── .gitattributes
├── src
├── icon.png
├── Atata.Configuration.Json
│ ├── GlobalUsings.cs
│ ├── ProxyJsonSection.cs
│ ├── Models
│ │ ├── Attributes
│ │ │ ├── AttributeJsonSection.cs
│ │ │ ├── AssemblyAttributesJsonSection.cs
│ │ │ ├── PropertyAttributesJsonSection.cs
│ │ │ ├── ComponentAttributesJsonSection.cs
│ │ │ └── AttributesJsonSection.cs
│ │ ├── Screenshots
│ │ │ ├── ScreenshotStrategyJsonSection.cs
│ │ │ └── ScreenshotsJsonSection.cs
│ │ ├── PageSnapshots
│ │ │ ├── PageSnapshotStrategyJsonSection.cs
│ │ │ └── PageSnapshotsJsonSection.cs
│ │ ├── BrowserLogs
│ │ │ └── BrowserLogsJsonSection.cs
│ │ └── EventSubscriptionJsonSection.cs
│ ├── JsonConfig.cs
│ ├── DriverPerformanceLoggingPreferencesJsonSection.cs
│ ├── DriverServiceJsonSection.cs
│ ├── AndroidOptionsJsonSection.cs
│ ├── Mapping
│ │ ├── IDriverJsonMapper.cs
│ │ ├── SafariDriverJsonMapper.cs
│ │ ├── EdgeDriverJsonMapper.cs
│ │ ├── ChromeDriverJsonMapper.cs
│ │ ├── InternetExplorerDriverJsonMapper.cs
│ │ ├── DriverJsonMapperAliases.cs
│ │ ├── RemoteDriverJsonMapper.cs
│ │ ├── AttributeMapper.cs
│ │ ├── ChromiumDriverJsonMapper`3.cs
│ │ ├── EventSubscriptionMapper.cs
│ │ ├── DriverJsonMapper`3.cs
│ │ ├── FirefoxDriverJsonMapper.cs
│ │ └── JsonConfigMapper.cs
│ ├── DriverProfileJsonSection.cs
│ ├── EventHandlers
│ │ ├── InitCurrentJsonConfigEventHandler`1.cs
│ │ ├── ResetCurrentJsonConfigEventHandler`1.cs
│ │ ├── LogJsonConfigPathEventHandler.cs
│ │ └── LogConfigurationWarningsEventHandler.cs
│ ├── LogConsumerJsonSection.cs
│ ├── JsonConverterWithoutPopulation.cs
│ ├── ConfigurationException.cs
│ ├── JsonConfigContractResolver.cs
│ ├── DriverJsonSection.cs
│ ├── JsonSection.cs
│ ├── Atata.Configuration.Json.csproj
│ ├── DriverOptionsJsonSection.cs
│ ├── JsonConfigManager`1.cs
│ ├── Extensions
│ │ ├── JsonAtataContextBuilderExtensions.cs
│ │ └── CheckExtensions.cs
│ ├── JsonConfigFile.cs
│ ├── JsonConfig`1.cs
│ └── GlobalSuppressions.cs
└── Directory.Build.props
├── test
└── Atata.Configuration.Json.Tests
│ ├── Configs
│ ├── RemoteTypeless.json
│ ├── EnvironmentVariables.json
│ ├── EventSubscriptions
│ │ ├── Empty.json
│ │ ├── HandlerType_Invalid.json
│ │ ├── HandlerType.json
│ │ ├── EventTypeAndHandlerType.json
│ │ └── EventTypeAndHandlerTypeAndArgument.json
│ ├── BrowserLogs.json
│ ├── RemoteFirefox.json
│ ├── DebugLogConsumers.json
│ ├── CommonDriverProperties.json
│ ├── TraceLogConsumers.json
│ ├── Remote.json
│ ├── Attributes
│ │ ├── Component.json
│ │ ├── Assembly.json
│ │ ├── Property.json
│ │ └── Global.json
│ ├── RemoteChrome.json
│ ├── MultipleDrivers.json
│ ├── CustomSettingsOverride2.json
│ ├── Edge.json
│ ├── VerificationProperties.json
│ ├── CustomSettingsOverride.json
│ ├── StandardSettings.json
│ ├── CustomSettings.json
│ ├── InternetExplorer.json
│ ├── LogConsumers.json
│ ├── Firefox.json
│ ├── Chrome+NUnit.json
│ └── Chrome.json
│ ├── Atata.json
│ ├── Atata.QA.json
│ ├── Simple.json
│ ├── Simple.QA.json
│ ├── GlobalUsings.cs
│ ├── Overrides
│ ├── EdgeDriverJsonMapperOverride.cs
│ ├── ChromeDriverJsonMapperOverride.cs
│ ├── FirefoxDriverJsonMapperOverride.cs
│ ├── RemoteDriverJsonMapperOverride.cs
│ ├── InternetExplorerDriverJsonMapperOverride.cs
│ ├── RemoteDriverAtataContextBuilderOverride.cs
│ ├── EdgeAtataContextBuilderOverride.cs
│ ├── ChromeAtataContextBuilderOverride.cs
│ ├── FirefoxAtataContextBuilderOverride.cs
│ ├── InternetExplorerAtataContextBuilderOverride.cs
│ ├── RemoteDriverContext.cs
│ └── DriverContext`2.cs
│ ├── TestFixture.cs
│ ├── BrowserLogsTests.cs
│ ├── CommonDriverPropertiesTests.cs
│ ├── SetUpFixture.cs
│ ├── GlobalSuppressions.cs
│ ├── CustomJsonConfig.cs
│ ├── EnvironmentVariableTests.cs
│ ├── StandardSettingsTests.cs
│ ├── CustomJsonConfigTests.cs
│ ├── EventSubscriptionsTests.cs
│ ├── LogConsumerTests.cs
│ ├── ConfigFilePathTests.cs
│ ├── AttributeTests.cs
│ ├── GeneralSettingsTests.cs
│ ├── CustomSettingsTests.cs
│ └── Atata.Configuration.Json.Tests.csproj
├── Directory.Build.targets
├── azure-pipelines.runsettings
├── CHANGELOG.md
├── azure-pipelines.release.yml
├── Directory.Build.props
├── en-US_User.dic
├── azure-pipelines.codecoverage.runsettings
├── azure-pipelines.yml
├── CONTRIBUTING.md
├── Atata.Configuration.Json.sln
├── CODE_OF_CONDUCT.md
├── .gitignore
└── LICENSE
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.sln text eol=crlf
3 |
--------------------------------------------------------------------------------
/src/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atata-framework/atata-configuration-json/HEAD/src/icon.png
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/RemoteTypeless.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "options": {
4 | }
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/EnvironmentVariables.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "{env:url_prefix}/{env:url_suffix}"
3 | }
4 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/EventSubscriptions/Empty.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventSubscriptions": [
3 | {
4 | }
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Atata.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "Chrome"
4 | },
5 | "baseUrl": "http://localhost:7575/atata"
6 | }
7 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | global using System.Reflection;
2 | global using System.Text.RegularExpressions;
3 | global using Newtonsoft.Json;
4 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Atata.QA.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "Chrome"
4 | },
5 | "baseUrl": "http://localhost:7575/atata.qa"
6 | }
7 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/BrowserLogs.json:
--------------------------------------------------------------------------------
1 | {
2 | "browserLogs": {
3 | "log": true,
4 | "minLevelOfWarning": "warn"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/RemoteFirefox.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "options": {
4 | "type": "firefox"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Simple.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "Chrome"
4 | },
5 | "baseUrl": "http://localhost:7575/simple"
6 | }
7 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Simple.QA.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "Chrome"
4 | },
5 | "baseUrl": "http://localhost:7575/simple.qa"
6 | }
7 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/DebugLogConsumers.json:
--------------------------------------------------------------------------------
1 | {
2 | "logConsumers": [
3 | {
4 | "type": "debug",
5 | "separator": " - "
6 | }
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/Directory.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(NoWarn);CA2007
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/CommonDriverProperties.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "chrome",
4 | "createRetries": 7,
5 | "initialHealthCheck": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/ProxyJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class ProxyJsonSection : JsonSection
4 | {
5 | public string[] BypassAddresses { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/EventSubscriptions/HandlerType_Invalid.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventSubscriptions": [
3 | {
4 | "handlerType": "EventSubscriptionsTests"
5 | }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/EventSubscriptions/HandlerType.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventSubscriptions": [
3 | {
4 | "handlerType": "EventSubscriptionsTests+TestEventHandler"
5 | }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/Attributes/AttributeJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class AttributeJsonSection : JsonSection
4 | {
5 | public string Type { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/JsonConfig.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | ///
4 | /// Represents JSON configuration.
5 | ///
6 | public class JsonConfig : JsonConfig
7 | {
8 | }
9 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/TraceLogConsumers.json:
--------------------------------------------------------------------------------
1 | {
2 | "logConsumers": [
3 | {
4 | "type": "trace",
5 | "minLevel": "Trace",
6 | "sectionEnd": "include"
7 | }
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/Screenshots/ScreenshotStrategyJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public sealed class ScreenshotStrategyJsonSection : JsonSection
4 | {
5 | public string Type { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/PageSnapshots/PageSnapshotStrategyJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public sealed class PageSnapshotStrategyJsonSection : JsonSection
4 | {
5 | public string Type { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/Remote.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "remoteAddress": "http://127.0.0.1:8888/wd/hub",
4 | "commandTimeout": 100,
5 | "options": {
6 | "type": "chrome"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/DriverPerformanceLoggingPreferencesJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class DriverPerformanceLoggingPreferencesJsonSection : JsonSection
4 | {
5 | public string[] TracingCategories { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/BrowserLogs/BrowserLogsJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public sealed class BrowserLogsJsonSection
4 | {
5 | public bool Log { get; set; }
6 |
7 | public LogLevel? MinLevelOfWarning { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | global using FluentAssertions;
2 | global using FluentAssertions.Execution;
3 | global using Newtonsoft.Json;
4 | global using NUnit.Framework;
5 | global using NUnit.Framework.Internal;
6 | global using OpenQA.Selenium;
7 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/DriverServiceJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class DriverServiceJsonSection : JsonSection
4 | {
5 | public string DriverPath { get; set; }
6 |
7 | public string DriverExecutableFileName { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/EventSubscriptionJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class EventSubscriptionJsonSection : JsonSection
4 | {
5 | public string EventType { get; set; }
6 |
7 | public string HandlerType { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/azure-pipelines.runsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | FullName
7 | cat != 'Unstable'
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/Attributes/AssemblyAttributesJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class AssemblyAttributesJsonSection
4 | {
5 | public string Name { get; set; }
6 |
7 | public AttributeJsonSection[] Attributes { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/Attributes/PropertyAttributesJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class PropertyAttributesJsonSection
4 | {
5 | public string Name { get; set; }
6 |
7 | public AttributeJsonSection[] Attributes { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/EventSubscriptions/EventTypeAndHandlerType.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventSubscriptions": [
3 | {
4 | "eventType": "EventSubscriptionsTests+TestEvent",
5 | "handlerType": "EventSubscriptionsTests+TestEventHandler"
6 | }
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/Screenshots/ScreenshotsJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public sealed class ScreenshotsJsonSection
4 | {
5 | public string FileNameTemplate { get; set; }
6 |
7 | public ScreenshotStrategyJsonSection Strategy { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/PageSnapshots/PageSnapshotsJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public sealed class PageSnapshotsJsonSection
4 | {
5 | public string FileNameTemplate { get; set; }
6 |
7 | public PageSnapshotStrategyJsonSection Strategy { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/AndroidOptionsJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class AndroidOptionsJsonSection : JsonSection
4 | {
5 | public string AndroidPackage { get; set; }
6 |
7 | // Firefox specific.
8 | public string[] AndroidIntentArguments { get; set; }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/IDriverJsonMapper.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public interface IDriverJsonMapper
6 | {
7 | void Map(DriverJsonSection section, AtataContextBuilder builder);
8 |
9 | DriverOptions CreateOptions(DriverOptionsJsonSection section);
10 | }
11 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/EventSubscriptions/EventTypeAndHandlerTypeAndArgument.json:
--------------------------------------------------------------------------------
1 | {
2 | "eventSubscriptions": [
3 | {
4 | "eventType": "EventSubscriptionsTests+TestEvent",
5 | "handlerType": "EventSubscriptionsTests+TestEventHandler",
6 | "someName": "TestName"
7 | }
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/Attributes/Component.json:
--------------------------------------------------------------------------------
1 | {
2 | "attributes": {
3 | "component": [
4 | {
5 | "type": "OrdinaryPage",
6 | "attributes": [
7 | {
8 | "type": "url",
9 | "value": "/some-url"
10 | }
11 | ]
12 | }
13 | ]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/RemoteChrome.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "remote",
4 | "options": {
5 | "type": "chrome",
6 | "LeaveBrowserRunning": true,
7 | "additionalBrowserOptions": {
8 | "cap1": true,
9 | "cap2": 5,
10 | "cap3": "str"
11 | }
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/MultipleDrivers.json:
--------------------------------------------------------------------------------
1 | {
2 | "drivers": [
3 | {
4 | "type": "firefox",
5 | "options": {
6 | "arguments": [ "--headless" ]
7 | }
8 | },
9 | {
10 | "type": "chrome",
11 | "options": {
12 | "arguments": [ "headless=new" ]
13 | }
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/Attributes/ComponentAttributesJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class ComponentAttributesJsonSection
4 | {
5 | public string Type { get; set; }
6 |
7 | public AttributeJsonSection[] Attributes { get; set; }
8 |
9 | public PropertyAttributesJsonSection[] Properties { get; set; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Models/Attributes/AttributesJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class AttributesJsonSection
4 | {
5 | public List Global { get; set; }
6 |
7 | public List Assembly { get; set; }
8 |
9 | public List Component { get; set; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/DriverProfileJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class DriverProfileJsonSection : JsonSection
4 | {
5 | public string ProfileDirectory { get; set; }
6 |
7 | public bool? DeleteSourceOnClean { get; set; }
8 |
9 | public JsonSection Preferences { get; set; }
10 |
11 | public string[] Extensions { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/EdgeDriverJsonMapperOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class EdgeDriverJsonMapperOverride : EdgeDriverJsonMapper
4 | {
5 | protected override EdgeAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
6 | builder.UseDriver(new EdgeAtataContextBuilderOverride(builder.BuildingContext));
7 | }
8 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/ChromeDriverJsonMapperOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class ChromeDriverJsonMapperOverride : ChromeDriverJsonMapper
4 | {
5 | protected override ChromeAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
6 | builder.UseDriver(new ChromeAtataContextBuilderOverride(builder.BuildingContext));
7 | }
8 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/FirefoxDriverJsonMapperOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class FirefoxDriverJsonMapperOverride : FirefoxDriverJsonMapper
4 | {
5 | protected override FirefoxAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
6 | builder.UseDriver(new FirefoxAtataContextBuilderOverride(builder.BuildingContext));
7 | }
8 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/SafariDriverJsonMapper.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium.Safari;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public class SafariDriverJsonMapper : DriverJsonMapper
6 | {
7 | protected override SafariAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
8 | builder.UseSafari();
9 | }
10 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/RemoteDriverJsonMapperOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class RemoteDriverJsonMapperOverride : RemoteDriverJsonMapper
4 | {
5 | protected override RemoteDriverAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
6 | builder.UseDriver(new RemoteDriverAtataContextBuilderOverride(builder.BuildingContext));
7 | }
8 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/EventHandlers/InitCurrentJsonConfigEventHandler`1.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | internal sealed class InitCurrentJsonConfigEventHandler : IEventHandler
4 | where TConfig : JsonConfig
5 | {
6 | public void Handle(AtataContextInitStartedEvent eventData, AtataContext context) =>
7 | JsonConfigManager.InitCurrentValue();
8 | }
9 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/EventHandlers/ResetCurrentJsonConfigEventHandler`1.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | internal sealed class ResetCurrentJsonConfigEventHandler : IEventHandler
4 | where TConfig : JsonConfig
5 | {
6 | public void Handle(AtataContextDeInitCompletedEvent eventData, AtataContext context) =>
7 | JsonConfigManager.ResetCurrentValue();
8 | }
9 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/InternetExplorerDriverJsonMapperOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class InternetExplorerDriverJsonMapperOverride : InternetExplorerDriverJsonMapper
4 | {
5 | protected override InternetExplorerAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
6 | builder.UseDriver(new InternetExplorerAtataContextBuilderOverride(builder.BuildingContext));
7 | }
8 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/CustomSettingsOverride2.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "Chrome",
4 | "options": {
5 | "arguments": [ "headless=new" ]
6 | }
7 | },
8 | "baseUrl": "https://demo.atata.io/override2",
9 | "stringProperty": "str3",
10 | "stringArrayValues": [ "str5" ],
11 | "stringListValues": [ "str5" ],
12 | "items": [
13 | {
14 | "name": "item4",
15 | "value": 12
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/Edge.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "edge",
4 | "options": {
5 | "additionalOptions": {
6 | "cap1": true,
7 | "cap2": 5,
8 | "cap3": "str"
9 | },
10 | "pageLoadStrategy": "eager"
11 | },
12 | "service": {
13 | "driverPath": "{env:EdgeDriver}",
14 | "driverExecutableFileName": "msedgedriver.exe",
15 | "allowedIPAddresses": "ips"
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/VerificationProperties.json:
--------------------------------------------------------------------------------
1 | {
2 | "assertionExceptionType": "NUnit.Framework.AssertionException, nunit.framework",
3 | "aggregateAssertionExceptionType": "NUnit.Framework.MultipleAssertException, nunit.framework",
4 | "aggregateAssertionStrategyType": "Atata.NUnitAggregateAssertionStrategy, Atata",
5 | "warningReportStrategyType": "Atata.NUnitWarningReportStrategy",
6 | "assertionFailureReportStrategyType": "NUnitAssertionFailureReportStrategy"
7 | }
8 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/TestFixture.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | [TestFixture]
4 | public class TestFixture
5 | {
6 | [TearDown]
7 | public virtual void TearDown()
8 | {
9 | AtataContext.Current?.Dispose();
10 |
11 | JsonConfig.Current = null;
12 | JsonConfig.Global = null;
13 | CustomJsonConfig.Current = null;
14 | CustomJsonConfig.Global = null;
15 |
16 | AtataContext.GlobalConfiguration.Clear();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/LogConsumerJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class LogConsumerJsonSection : JsonSection
4 | {
5 | public string Type { get; set; }
6 |
7 | public LogLevel? MinLevel { get; set; }
8 |
9 | public LogSectionEndOption? SectionEnd { get; set; }
10 |
11 | public string MessageNestingLevelIndent { get; set; }
12 |
13 | public string MessageStartSectionPrefix { get; set; }
14 |
15 | public string MessageEndSectionPrefix { get; set; }
16 | }
17 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/CustomSettingsOverride.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "Chrome",
4 | "options": {
5 | "arguments": [ "headless=new" ]
6 | }
7 | },
8 | "baseUrl": "https://demo.atata.io/override",
9 | "stringProperty": "str2",
10 | "stringArrayValues": [ "str4" ],
11 | "stringListValues": [ "str4" ],
12 | "section": {
13 | "boolProperty": false
14 | },
15 | "items": [
16 | {
17 | "name": "item3",
18 | "value": 9
19 | }
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/EventHandlers/LogJsonConfigPathEventHandler.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | internal sealed class LogJsonConfigPathEventHandler : IEventHandler
4 | {
5 | private readonly string _configPath;
6 |
7 | internal LogJsonConfigPathEventHandler(string configPath) =>
8 | _configPath = configPath;
9 |
10 | public void Handle(AtataContextInitStartedEvent eventData, AtataContext context) =>
11 | context.Log.Trace($"Use: \"{_configPath}\" config");
12 | }
13 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/BrowserLogsTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public sealed class BrowserLogsTests : TestFixture
4 | {
5 | [Test]
6 | public void WithAllPropertiesSet()
7 | {
8 | var builder = AtataContext.Configure().
9 | ApplyJsonConfig($"Configs/BrowserLogs");
10 |
11 | builder.BuildingContext.ToResultSubject()
12 | .ValueOf(x => x.BrowserLogs.Log).Should.BeTrue()
13 | .ValueOf(x => x.BrowserLogs.MinLevelOfWarning).Should.Be(LogLevel.Warn);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/Attributes/Assembly.json:
--------------------------------------------------------------------------------
1 | {
2 | "attributes": {
3 | "assembly": [
4 | {
5 | "name": "Atata.Configuration.Json.Tests",
6 | "attributes": [
7 | {
8 | "type": "FindById",
9 | "value": "some-id"
10 | }
11 | ]
12 | },
13 | {
14 | "name": "Atata",
15 | "attributes": [
16 | {
17 | "type": "FindByName",
18 | "values": "some-name"
19 | }
20 | ]
21 | }
22 | ]
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/JsonConverterWithoutPopulation.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class JsonConverterWithoutPopulation : JsonConverter
4 | {
5 | public override bool CanConvert(Type objectType) =>
6 | true;
7 |
8 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) =>
9 | serializer.Deserialize(reader, objectType);
10 |
11 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) =>
12 | serializer.Serialize(writer, value);
13 | }
14 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/StandardSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "chrome",
4 | "options": {
5 | "arguments": [ "headless" ]
6 | }
7 | },
8 | "driverInitializationStage": "OnDemand",
9 | "baseUrl": "https://demo.atata.io/",
10 | "artifactsPathTemplate": "{test-suite-name-sanitized:/*}{test-name-sanitized:/prefix_*_postfix}",
11 |
12 | "variables": {
13 | "customIntVar": 7,
14 | "customStringVar": "strvar"
15 | },
16 |
17 | "domTestIdAttributeName": "data-autoid",
18 | "domTestIdAttributeDefaultCase": "midSentence",
19 |
20 | "useAllNUnitFeatures": true
21 | }
22 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/EventHandlers/LogConfigurationWarningsEventHandler.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | internal sealed class LogConfigurationWarningsEventHandler : IEventHandler
4 | {
5 | private readonly IEnumerable _warningMessages;
6 |
7 | internal LogConfigurationWarningsEventHandler(IEnumerable warningMessages) =>
8 | _warningMessages = warningMessages;
9 |
10 | public void Handle(AtataContextInitStartedEvent eventData, AtataContext context)
11 | {
12 | foreach (string warningMessage in _warningMessages)
13 | context.Log.Warn(warningMessage);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/ConfigurationException.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | [Serializable]
6 | public class ConfigurationException : Exception
7 | {
8 | public ConfigurationException()
9 | {
10 | }
11 |
12 | public ConfigurationException(string message)
13 | : base(message)
14 | {
15 | }
16 |
17 | public ConfigurationException(string message, Exception innerException)
18 | : base(message, innerException)
19 | {
20 | }
21 |
22 | protected ConfigurationException(SerializationInfo info, StreamingContext context)
23 | : base(info, context)
24 | {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/CustomSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "Chrome",
4 | "options": {
5 | "arguments": [ "start-maximized" ]
6 | }
7 | },
8 | "baseUrl": "https://demo.atata.io/",
9 |
10 | "intProperty": 5,
11 | "stringProperty": "str",
12 | "boolProperty": true,
13 | "stringArrayValues": [ "str1", "str2", "str3" ],
14 | "stringListValues": [ "str1", "str2", "str3" ],
15 | "section": {
16 | "stringProperty": "section_str",
17 | "boolProperty": true
18 | },
19 | "items": [
20 | {
21 | "name": "item1",
22 | "value": 5
23 | },
24 | {
25 | "name": "item2",
26 | "value": 7
27 | }
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/Attributes/Property.json:
--------------------------------------------------------------------------------
1 | {
2 | "attributes": {
3 | "component": [
4 | {
5 | "type": "OrdinaryPage",
6 | "properties": [
7 | {
8 | "name": "Prop1",
9 | "attributes": [
10 | {
11 | "type": "FindById",
12 | "value": "some-id"
13 | }
14 | ]
15 | },
16 | {
17 | "name": "Prop2",
18 | "attributes": [
19 | {
20 | "type": "FindByName",
21 | "values": "some-name"
22 | }
23 | ]
24 | }
25 | ]
26 | }
27 | ]
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## [Unreleased]
9 |
10 | ### Changed
11 |
12 | - Upgrade Atata package reference to v3.8.0.
13 |
14 | ## [3.1.0] - 2025-10-26
15 |
16 | ### Changed
17 |
18 | - Upgrade Newtonsoft.Json package reference to v13.0.4.
19 | - Upgrade Atata package reference to v3.7.0.
20 |
21 | [Unreleased]: https://github.com/atata-framework/atata-configuration-json/compare/v3.1.0...HEAD
22 | [3.1.0]: https://github.com/atata-framework/atata-configuration-json/compare/v3.0.0...v3.1.0
23 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/CommonDriverPropertiesTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class CommonDriverPropertiesTests : TestFixture
4 | {
5 | private Subject _sut;
6 |
7 | [SetUp]
8 | public void SetUpSuite()
9 | {
10 | AtataContextBuilder builder = AtataContext.Configure()
11 | .ApplyJsonConfig("Configs/CommonDriverProperties.json");
12 |
13 | _sut = ((ChromeAtataContextBuilder)builder.BuildingContext.DriverFactoryToUse).ToSutSubject();
14 | }
15 |
16 | [Test]
17 | public void CreateRetries() =>
18 | _sut.ValueOf(x => x.CreateRetries).Should.Be(7);
19 |
20 | [Test]
21 | public void InitialHealthCheck() =>
22 | _sut.ValueOf(x => x.InitialHealthCheck).Should.BeTrue();
23 | }
24 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/JsonConfigContractResolver.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Serialization;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | internal sealed class JsonConfigContractResolver : DefaultContractResolver
6 | {
7 | public static JsonConfigContractResolver Instance { get; } = new JsonConfigContractResolver();
8 |
9 | protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
10 | {
11 | JsonProperty property = base.CreateProperty(member, memberSerialization);
12 |
13 | if (property.DeclaringType.IsGenericType && property.DeclaringType.GetGenericTypeDefinition() == typeof(JsonConfig<>) && property.PropertyName == nameof(JsonConfig.Driver))
14 | property.ShouldSerialize = _ => false;
15 |
16 | return property;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/DriverJsonSection.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class DriverJsonSection : JsonSection
4 | {
5 | public string Alias { get; set; }
6 |
7 | public string Type { get; set; }
8 |
9 | public int? CreateRetries { get; set; }
10 |
11 | public bool? InitialHealthCheck { get; set; }
12 |
13 | public string RemoteAddress { get; set; }
14 |
15 | public DriverOptionsJsonSection Options { get; set; }
16 |
17 | public DriverServiceJsonSection Service { get; set; }
18 |
19 | ///
20 | /// Gets or sets the command timeout in seconds.
21 | ///
22 | public double? CommandTimeout { get; set; }
23 |
24 | ///
25 | /// Gets or sets the ports to ignore.
26 | ///
27 | public int[] PortsToIgnore { get; set; }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/EdgeDriverJsonMapper.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium.Edge;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public class EdgeDriverJsonMapper : ChromiumDriverJsonMapper
6 | {
7 | protected override EdgeAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
8 | builder.UseEdge();
9 |
10 | protected override void MapOptions(DriverOptionsJsonSection section, EdgeOptions options)
11 | {
12 | base.MapOptions(section, options);
13 |
14 | if (section.AdditionalBrowserOptions != null)
15 | {
16 | foreach (var item in section.AdditionalBrowserOptions.ExtraPropertiesMap)
17 | options.AddAdditionalEdgeOption(item.Key, FillTemplateVariables(item.Value));
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/azure-pipelines.release.yml:
--------------------------------------------------------------------------------
1 | variables:
2 | netSdkVersion: '8.x'
3 |
4 | trigger: none
5 | pr: none
6 |
7 | jobs:
8 | - job: PublishPackage
9 | displayName: Publish package to NuGet
10 | pool:
11 | vmImage: windows-latest
12 | steps:
13 | - task: NuGetToolInstaller@1
14 | displayName: Install NuGet tool
15 | - task: UseDotNet@2
16 | displayName: Set up .NET SDK
17 | inputs:
18 | version: $(netSdkVersion)
19 | - task: DotNetCoreCLI@2
20 | displayName: Pack
21 | inputs:
22 | command: pack
23 | packagesToPack: 'src/*/*.csproj'
24 | configuration: 'Release'
25 | - task: NuGetCommand@2
26 | displayName: NuGet push
27 | inputs:
28 | command: push
29 | packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg'
30 | nuGetFeedType: 'external'
31 | publishFeedCredentials: 'NuGet'
32 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 13.0
5 | enable
6 | enable
7 | Yevgeniy Shunevych
8 | Yevgeniy Shunevych
9 | Atata Framework
10 | © 2016-2025 Yevgeniy Shunevych
11 | true
12 | true
13 | 8.0-Recommended
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/ChromeDriverJsonMapper.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium.Chrome;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public class ChromeDriverJsonMapper : ChromiumDriverJsonMapper
6 | {
7 | protected override ChromeAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
8 | builder.UseChrome();
9 |
10 | protected override void MapOptions(DriverOptionsJsonSection section, ChromeOptions options)
11 | {
12 | base.MapOptions(section, options);
13 |
14 | if (section.AdditionalBrowserOptions != null)
15 | {
16 | foreach (var item in section.AdditionalBrowserOptions.ExtraPropertiesMap)
17 | options.AddAdditionalChromeOption(item.Key, FillTemplateVariables(item.Value));
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/InternetExplorer.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "internetexplorer",
4 | "options": {
5 | "additionalOptions": {
6 | "globalcap1": true,
7 | "globalcap2": 5,
8 | "globalcap3": "str"
9 | },
10 | "additionalBrowserOptions": {
11 | "cap1": false,
12 | "cap2": 15,
13 | "cap3": "str2"
14 | },
15 | "proxy": {
16 | "socksVersion": 5,
17 | "socksProxy": "socks",
18 | "socksUserName": "name",
19 | "socksPassword": "pass"
20 | },
21 | "enableNativeEvents": false,
22 | "requireWindowFocus": true,
23 | "browserAttachTimeout": 2,
24 | "elementScrollBehavior": "Bottom"
25 | },
26 | "service": {
27 | "loggingLevel": "Debug"
28 | },
29 | "commandTimeout": 45
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/SetUpFixture.cs:
--------------------------------------------------------------------------------
1 | using Atata.WebDriverSetup;
2 |
3 | namespace Atata.Configuration.Json.Tests;
4 |
5 | [SetUpFixture]
6 | public class SetUpFixture
7 | {
8 | [OneTimeSetUp]
9 | public void SetUp()
10 | {
11 | DriverJsonMapperAliases.Register(DriverAliases.Remote);
12 | DriverJsonMapperAliases.Register(DriverAliases.Chrome);
13 | DriverJsonMapperAliases.Register(DriverAliases.Firefox);
14 | DriverJsonMapperAliases.Register(DriverAliases.InternetExplorer);
15 | DriverJsonMapperAliases.Register(DriverAliases.Edge);
16 |
17 | DriverSetup.AutoSetUp(BrowserNames.Chrome, BrowserNames.Firefox, BrowserNames.Edge);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | #pragma warning disable S103 // Lines should not be too long
4 |
5 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.Tests.CustomJsonConfig.StringArrayValues")]
6 | [assembly: SuppressMessage("Style", "IDE0038:Use pattern matching", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.Tests.EventSubscriptionsTests.EventTypeAndHandlerTypeAndArgument")]
7 | [assembly: SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty", Justification = "", Scope = "type", Target = "~T:Atata.Configuration.Json.Tests.EventSubscriptionsTests.TestEvent")]
8 |
9 | #pragma warning restore S103 // Lines should not be too long
10 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/LogConsumers.json:
--------------------------------------------------------------------------------
1 | {
2 | "logConsumers": [
3 | {
4 | "type": "debug",
5 | "separator": " - "
6 | },
7 | {
8 | "type": "trace",
9 | "minLevel": "Trace",
10 | "sectionEnd": "include",
11 | "messageNestingLevelIndent": "_ ",
12 | "messageStartSectionPrefix": "S:",
13 | "messageEndSectionPrefix": "E:"
14 | },
15 | {
16 | "type": "nunit",
17 | "minLevel": "info",
18 | "sectionEnd": "exclude"
19 | },
20 | {
21 | "type": "nlog",
22 | "minLevel": "warn",
23 | "sectionEnd": "includeForBlocks",
24 | "loggerName": "somelogger"
25 | },
26 | {
27 | "type": "Atata.Configuration.Json.Tests.LogConsumerTests+CustomLogConsumer, Atata.Configuration.Json.Tests",
28 | "minLevel": "error",
29 | "intProperty": 15
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/RemoteDriverAtataContextBuilderOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class RemoteDriverAtataContextBuilderOverride : RemoteDriverAtataContextBuilder
4 | {
5 | [ThreadStatic]
6 | private static RemoteDriverContext s_context;
7 |
8 | public RemoteDriverAtataContextBuilderOverride(AtataBuildingContext buildingContext)
9 | : base(buildingContext)
10 | {
11 | }
12 |
13 | public static RemoteDriverContext Context =>
14 | s_context ??= new RemoteDriverContext();
15 |
16 | protected override IWebDriver CreateDriver(Uri remoteAddress, ICapabilities capabilities, TimeSpan commandTimeout)
17 | {
18 | Context.Set(remoteAddress, capabilities, commandTimeout);
19 |
20 | return Context.ReturnsNull ? null : base.CreateDriver(remoteAddress, capabilities, commandTimeout);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/InternetExplorerDriverJsonMapper.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium.IE;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public class InternetExplorerDriverJsonMapper : DriverJsonMapper
6 | {
7 | protected override InternetExplorerAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
8 | builder.UseInternetExplorer();
9 |
10 | protected override void MapOptions(DriverOptionsJsonSection section, InternetExplorerOptions options)
11 | {
12 | base.MapOptions(section, options);
13 |
14 | if (section.AdditionalBrowserOptions != null)
15 | {
16 | foreach (var item in section.AdditionalBrowserOptions.ExtraPropertiesMap)
17 | options.AddAdditionalInternetExplorerOption(item.Key, FillTemplateVariables(item.Value));
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/Firefox.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "firefox",
4 | "options": {
5 | "additionalOptions": {
6 | "globalcap1": true,
7 | "globalcap2": 5,
8 | "globalcap3": "str"
9 | },
10 | "additionalBrowserOptions": {
11 | "cap1": false,
12 | "cap2": 15,
13 | "cap3": "str2"
14 | },
15 | "proxy": {
16 | "kind": "ProxyAutoConfigure"
17 | },
18 | "arguments": [ "--start-maximized" ],
19 | "preferences": {
20 | "pref1": true,
21 | "pref2": 5,
22 | "pref3": "str"
23 | },
24 | "logLevel": "warn",
25 | "androidOptions": {
26 | "androidPackage": "pack1",
27 | "androidActivity": "act1",
28 | "androidIntentArguments": [ "arg1", "arg2" ]
29 | }
30 | },
31 | "service": {
32 | "host": "127.0.0.5"
33 | },
34 | "commandTimeout": 0.95
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/CustomJsonConfig.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class CustomJsonConfig : JsonConfig
4 | {
5 | public int IntProperty { get; set; }
6 |
7 | public string StringProperty { get; set; }
8 |
9 | public bool BoolProperty { get; set; }
10 |
11 | public bool BoolProperty2 { get; set; }
12 |
13 | public string[] StringArrayValues { get; set; }
14 |
15 | public List StringListValues { get; } = [];
16 |
17 | public CustomSection Section { get; set; }
18 |
19 | public List Items { get; } = [];
20 |
21 | public class CustomSection
22 | {
23 | public string StringProperty { get; set; }
24 |
25 | public bool BoolProperty { get; set; }
26 | }
27 |
28 | public class CustomItemSection
29 | {
30 | public string Name { get; set; }
31 |
32 | public int Value { get; set; }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/EdgeAtataContextBuilderOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | using TOptions = OpenQA.Selenium.Edge.EdgeOptions;
4 | using TService = OpenQA.Selenium.Edge.EdgeDriverService;
5 |
6 | public class EdgeAtataContextBuilderOverride : EdgeAtataContextBuilder
7 | {
8 | [ThreadStatic]
9 | private static DriverContext s_context;
10 |
11 | public EdgeAtataContextBuilderOverride(AtataBuildingContext buildingContext)
12 | : base(buildingContext)
13 | {
14 | }
15 |
16 | public static DriverContext Context =>
17 | s_context ??= new DriverContext();
18 |
19 | protected override IWebDriver CreateDriver(TService service, TOptions options, TimeSpan commandTimeout)
20 | {
21 | Context.Set(service, options, commandTimeout);
22 |
23 | return Context.ReturnsNull ? null : base.CreateDriver(service, options, commandTimeout);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/ChromeAtataContextBuilderOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | using TOptions = OpenQA.Selenium.Chrome.ChromeOptions;
4 | using TService = OpenQA.Selenium.Chrome.ChromeDriverService;
5 |
6 | public class ChromeAtataContextBuilderOverride : ChromeAtataContextBuilder
7 | {
8 | [ThreadStatic]
9 | private static DriverContext s_context;
10 |
11 | public ChromeAtataContextBuilderOverride(AtataBuildingContext buildingContext)
12 | : base(buildingContext)
13 | {
14 | }
15 |
16 | public static DriverContext Context =>
17 | s_context ??= new DriverContext();
18 |
19 | protected override IWebDriver CreateDriver(TService service, TOptions options, TimeSpan commandTimeout)
20 | {
21 | Context.Set(service, options, commandTimeout);
22 |
23 | return Context.ReturnsNull ? null : base.CreateDriver(service, options, commandTimeout);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/FirefoxAtataContextBuilderOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | using TOptions = OpenQA.Selenium.Firefox.FirefoxOptions;
4 | using TService = OpenQA.Selenium.Firefox.FirefoxDriverService;
5 |
6 | public class FirefoxAtataContextBuilderOverride : FirefoxAtataContextBuilder
7 | {
8 | [ThreadStatic]
9 | private static DriverContext s_context;
10 |
11 | public FirefoxAtataContextBuilderOverride(AtataBuildingContext buildingContext)
12 | : base(buildingContext)
13 | {
14 | }
15 |
16 | public static DriverContext Context =>
17 | s_context ??= new DriverContext();
18 |
19 | protected override IWebDriver CreateDriver(TService service, TOptions options, TimeSpan commandTimeout)
20 | {
21 | Context.Set(service, options, commandTimeout);
22 |
23 | return Context.ReturnsNull ? null : base.CreateDriver(service, options, commandTimeout);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/InternetExplorerAtataContextBuilderOverride.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | using TOptions = OpenQA.Selenium.IE.InternetExplorerOptions;
4 | using TService = OpenQA.Selenium.IE.InternetExplorerDriverService;
5 |
6 | public class InternetExplorerAtataContextBuilderOverride : InternetExplorerAtataContextBuilder
7 | {
8 | [ThreadStatic]
9 | private static DriverContext s_context;
10 |
11 | public InternetExplorerAtataContextBuilderOverride(AtataBuildingContext buildingContext)
12 | : base(buildingContext)
13 | {
14 | }
15 |
16 | public static DriverContext Context =>
17 | s_context ??= new DriverContext();
18 |
19 | protected override IWebDriver CreateDriver(TService service, TOptions options, TimeSpan commandTimeout)
20 | {
21 | Context.Set(service, options, commandTimeout);
22 |
23 | return Context.ReturnsNull ? null : base.CreateDriver(service, options, commandTimeout);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/en-US_User.dic:
--------------------------------------------------------------------------------
1 | app
2 | Atata
3 | autocomplete
4 | awaitable
5 | casted
6 | CDP
7 | checkbox
8 | checkboxes
9 | clearable
10 | CLI
11 | clickable
12 | combobox
13 | concat
14 | CSS
15 | Ctrl
16 | dasherize
17 | deinitialization
18 | deinitialize
19 | deinitialized
20 | deinitializes
21 | deinitializing
22 | dev
23 | dropdown
24 | enum
25 | enums
26 | fallback
27 | fieldset
28 | formattable
29 | href
30 | initializable
31 | JSON
32 | Kendo
33 | labelled
34 | localhost
35 | locator
36 | MCE
37 | metadata
38 | Mui
39 | multicast
40 | multiline
41 | mutex
42 | MVC
43 | namespace
44 | nav
45 | navbar
46 | NG
47 | NPM
48 | nullable
49 | polyline
50 | popup
51 | prepend
52 | refactor
53 | Reqnroll
54 | screenshot
55 | screenshots
56 | serializer
57 | sessionless
58 | stdout
59 | stderr
60 | stringifier
61 | sudo
62 | Summernote
63 | sut
64 | SVG
65 | teardown
66 | templated
67 | thru
68 | timestamp
69 | uncheck
70 | unconfigured
71 | unescaped
72 | uninstall
73 | uninstalls
74 | unsubscribe
75 | uri
76 | username
77 | UTC
78 | utils
79 | validator
80 | viewport
81 | Vue
82 | wildcard
83 | wildcards
84 | Xunit
85 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/Attributes/Global.json:
--------------------------------------------------------------------------------
1 | {
2 | "attributes": {
3 | "global": [
4 | {
5 | "type": "termFindSettings",
6 | "targetAttributeType": "FindById",
7 | "targetType": "Field`2",
8 | "excludeTargetType": "CheckBox`1",
9 | "case": "lowermerged"
10 | },
11 | {
12 | "type": "findSettings",
13 | "visibility": "any",
14 | "targetAttributeTypes": [
15 | "FindByClass",
16 | "FindByFieldSetAttribute",
17 | "Atata.FindByLabelAttribute, Atata"
18 | ],
19 | "targetTypes": [
20 | "Table`2",
21 | "Table`3"
22 | ],
23 | "excludeTargetNames": [
24 | "a",
25 | "b"
26 | ],
27 | "excludeTargetParentType": "Frame`1"
28 | },
29 | {
30 | "type": "FindById",
31 | "value": "some-id"
32 | },
33 | {
34 | "type": "FindByName",
35 | "values": [
36 | "name1",
37 | "name2"
38 | ]
39 | }
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/azure-pipelines.codecoverage.runsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | .*\.TestApp\.dll$
13 | .*\.UnitTests\.dll$
14 | .*\.IntegrationTests\.dll$
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | FullName
24 | cat != 'Unstable'
25 |
26 |
27 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/RemoteDriverContext.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class RemoteDriverContext
4 | {
5 | public bool ReturnsNull { get; set; }
6 |
7 | public Uri RemoteAddress { get; private set; }
8 |
9 | public ICapabilities Capabilities { get; private set; }
10 |
11 | public TimeSpan CommandTimeout { get; private set; }
12 |
13 | public void Set(Uri remoteAddress, ICapabilities capabilities, TimeSpan commandTimeout)
14 | {
15 | RemoteAddress = remoteAddress;
16 | Capabilities = capabilities;
17 | CommandTimeout = commandTimeout;
18 | }
19 |
20 | public IDisposable UseNullDriver()
21 | {
22 | ReturnsNull = true;
23 | return new DriverContextNullableSession(this);
24 | }
25 |
26 | private sealed class DriverContextNullableSession : IDisposable
27 | {
28 | private readonly RemoteDriverContext _context;
29 |
30 | public DriverContextNullableSession(RemoteDriverContext context) =>
31 | _context = context;
32 |
33 | public void Dispose() =>
34 | _context.ReturnsNull = false;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | Apache-2.0
7 | icon.png
8 | git
9 | true
10 | true
11 | snupkg
12 | true
13 |
14 |
15 |
16 | $(NoWarn);IDT001
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | variables:
2 | buildConfiguration: 'Release'
3 | netSdkVersion: '8.x'
4 |
5 | jobs:
6 | - job: BuildAndRunCodeAnalysis
7 | displayName: Build and run code analysis
8 | pool:
9 | vmImage: windows-latest
10 | steps:
11 | - task: UseDotNet@2
12 | displayName: Set up .NET SDK
13 | inputs:
14 | version: $(netSdkVersion)
15 | - task: DotNetCoreCLI@2
16 | displayName: Build solution with code analysis
17 | inputs:
18 | command: build
19 | projects: '*.sln'
20 | arguments: -c $(buildConfiguration) -warnaserror
21 |
22 | - job: RunTests
23 | displayName: Run tests
24 | pool:
25 | vmImage: windows-latest
26 | steps:
27 | - task: UseDotNet@2
28 | displayName: Set up .NET SDK
29 | inputs:
30 | version: $(netSdkVersion)
31 | - task: DotNetCoreCLI@2
32 | displayName: Build solution
33 | inputs:
34 | command: build
35 | projects: '*.sln'
36 | arguments: -c $(buildConfiguration) -p:RunCodeAnalysis=false
37 | - task: DotNetCoreCLI@2
38 | displayName: 'Run tests'
39 | timeoutInMinutes: 10
40 | inputs:
41 | command: test
42 | projects: 'test/*/*Tests.csproj'
43 | arguments: -c $(buildConfiguration) -s "azure-pipelines.runsettings" --no-build
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Overrides/DriverContext`2.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class DriverContext
4 | where TService : DriverService
5 | {
6 | public bool ReturnsNull { get; set; }
7 |
8 | public TService Service { get; private set; }
9 |
10 | public TOptions Options { get; private set; }
11 |
12 | public TimeSpan CommandTimeout { get; private set; }
13 |
14 | public void Set(TService service, TOptions options, TimeSpan commandTimeout)
15 | {
16 | Service = service;
17 | Options = options;
18 | CommandTimeout = commandTimeout;
19 | }
20 |
21 | public IDisposable UseNullDriver()
22 | {
23 | ReturnsNull = true;
24 | return new DriverContextNullableSession(this);
25 | }
26 |
27 | private sealed class DriverContextNullableSession : IDisposable
28 | {
29 | private readonly DriverContext _context;
30 |
31 | public DriverContextNullableSession(DriverContext context) =>
32 | _context = context;
33 |
34 | public void Dispose()
35 | {
36 | _context.ReturnsNull = false;
37 | _context.Service?.Dispose();
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/EnvironmentVariableTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class EnvironmentVariableTests : TestFixture
4 | {
5 | private const string Variable1Name = "url_prefix";
6 |
7 | private const string Variable2Name = "url_suffix";
8 |
9 | [SetUp]
10 | public void SetUp() =>
11 | Environment.SetEnvironmentVariable(Variable1Name, null);
12 |
13 | [Test]
14 | public void Found()
15 | {
16 | Environment.SetEnvironmentVariable(Variable1Name, "https://example.org", EnvironmentVariableTarget.Process);
17 | Environment.SetEnvironmentVariable(Variable2Name, "test", EnvironmentVariableTarget.Process);
18 |
19 | AtataContextBuilder builder = AtataContext.Configure()
20 | .ApplyJsonConfig("Configs/EnvironmentVariables");
21 |
22 | builder.BuildingContext.BaseUrl.Should().Be("https://example.org/test");
23 | }
24 |
25 | [Test]
26 | public void NotFound()
27 | {
28 | AtataContextBuilder builder = AtataContext.Configure();
29 |
30 | var exception = Assert.Throws(() =>
31 | builder.ApplyJsonConfig("Configs/EnvironmentVariables"));
32 |
33 | exception.Message.Should().Contain(Variable1Name);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/StandardSettingsTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | [TestFixture]
4 | public class StandardSettingsTests : TestFixture
5 | {
6 | [Test]
7 | public void Regular()
8 | {
9 | Subject result = AtataContext.Configure()
10 | .ApplyJsonConfig(@"Configs/StandardSettings.json")
11 | .Build()
12 | .ToResultSubject();
13 |
14 | result.ValueOf(x => x.DriverInitializationStage).Should.Be(AtataContextDriverInitializationStage.OnDemand);
15 |
16 | result.ValueOf(x => x.BaseUrl).Should.Be("https://demo.atata.io/");
17 |
18 | result.ValueOf(x => x.Artifacts.FullName.Value).Should.Be(
19 | Path.Combine(
20 | AtataContext.GlobalProperties.ArtifactsRootPath,
21 | nameof(StandardSettingsTests),
22 | $"prefix_{TestContext.CurrentContext.Test.Name}_postfix"));
23 |
24 | result.ValueOf(x => x.Variables["customIntVar"]).Should.Be(7L);
25 | result.ValueOf(x => x.Variables["customStringVar"]).Should.Be("strvar");
26 |
27 | result.ValueOf(x => x.DomTestIdAttributeName).Should.Be("data-autoid");
28 | result.ValueOf(x => x.DomTestIdAttributeDefaultCase).Should.Be(TermCase.MidSentence);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/JsonSection.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Linq;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | ///
6 | /// Represents JSON section.
7 | ///
8 | public class JsonSection
9 | {
10 | [JsonExtensionData]
11 | public Dictionary AdditionalProperties { get; } = [];
12 |
13 | [JsonIgnore]
14 | public Dictionary ExtraPropertiesMap => AdditionalProperties?.ToDictionary(x => x.Key, x => ConvertJToken(x.Value));
15 |
16 | private static object ConvertJToken(JToken token) =>
17 | token.Type switch
18 | {
19 | JTokenType.None or JTokenType.Null or JTokenType.Undefined =>
20 | null,
21 | JTokenType.Integer =>
22 | (int)token,
23 | JTokenType.Float =>
24 | (double)token,
25 | JTokenType.Boolean =>
26 | (bool)token,
27 | JTokenType.TimeSpan =>
28 | (TimeSpan)token,
29 | JTokenType.Array =>
30 | ((JArray)token).Select(ConvertJToken).ToArray(),
31 | JTokenType.Object =>
32 | ((IEnumerable>)token).ToDictionary(x => x.Key, x => ConvertJToken(x.Value)),
33 | _ => (string)token,
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | ## Issue Contributing
4 |
5 | Feel free to report issues on GitHub.
6 | Any issues or questions can also be targeted to any communication channel defined on [Atata Contact](https://atata.io/contact/) page.
7 |
8 | ## Code Contributing
9 |
10 | ### Development Prerequisites
11 |
12 | - Visual Studio 2022 or JetBrains Rider.
13 |
14 | ### Setting Up For Development
15 |
16 | In order to set up this project for further contributing do the following:
17 |
18 | - Fork the repository.
19 | - Clone the forked repository locally.
20 | - Open `.sln` file located in the root of the repository.
21 | - Run tests through IDE or with `dotnet test` command.
22 |
23 | ### Development
24 |
25 | Please follow the rules during development:
26 |
27 | - Fix (or suppress in rare cases) all code analysis warnings.
28 | - Ensure that newly added public classes and members have XML documentation comments.
29 | This does not apply to classes in test projects.
30 | - Run all tests in order to ensure the changes don't break anything.
31 | - Try to add/update tests respectively.
32 | - Follow [Semantic Versioning 2.0](https://semver.org/) during the source changes.
33 | - [Create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) when it is done.
34 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Atata.Configuration.Json.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | 12.0
6 | disable
7 | 3.1.0
8 | C#/.NET package for Atata configuration through JSON files.
9 |
10 | The package targets .NET Standard 2.0, which supports .NET 5+, .NET Framework 4.6.1+ and .NET Core/Standard 2.0+.
11 |
12 | Atata.Configuration.Json on GitHub: https://github.com/atata-framework/atata-configuration-json
13 | Atata Framework documentation: https://atata.io
14 |
15 | Features:
16 | - Full configuration of Atata context via JSON file
17 | - Custom settings
18 | - Multi-driver configuration
19 | - Merged configuration via multiple files
20 | - Multiple environments support
21 | https://github.com/atata-framework/atata-configuration-json
22 | Atata.Configuration.Json
23 | atata automation testing test selenium webdriver browser configuration
24 | https://github.com/atata-framework/atata-configuration-json/releases/tag/v3.1.0
25 | https://github.com/atata-framework/atata-configuration-json
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/CustomJsonConfigTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | [TestFixture]
4 | public class CustomJsonConfigTests : TestFixture
5 | {
6 | [Test]
7 | public void Default()
8 | {
9 | string jsonContent = File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Configs/CustomSettings.json"));
10 | JsonConfig config = JsonConvert.DeserializeObject(jsonContent);
11 |
12 | AtataContextBuilder builder = AtataContext.Configure()
13 | .ApplyJsonConfig(config);
14 |
15 | JsonConfig.Current.Should().BeNull();
16 |
17 | config.BaseUrl.Should().Be("https://demo.atata.io/");
18 |
19 | builder.BuildingContext.DriverFactories.Should().HaveCount(1);
20 | builder.BuildingContext.BaseUrl.Should().Be("https://demo.atata.io/");
21 | }
22 |
23 | [Test]
24 | public void Custom()
25 | {
26 | string jsonContent = File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Configs/CustomSettings.json"));
27 | CustomJsonConfig config = JsonConvert.DeserializeObject(jsonContent);
28 |
29 | AtataContextBuilder builder = AtataContext.Configure()
30 | .ApplyJsonConfig(config);
31 |
32 | CustomJsonConfig.Current.Should().BeNull();
33 |
34 | config.BaseUrl.Should().Be("https://demo.atata.io/");
35 | config.IntProperty.Should().Be(5);
36 | config.StringArrayValues.Should().Equal("str1", "str2", "str3");
37 |
38 | builder.BuildingContext.DriverFactories.Should().HaveCount(1);
39 | builder.BuildingContext.BaseUrl.Should().Be("https://demo.atata.io/");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/DriverJsonMapperAliases.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public static class DriverJsonMapperAliases
4 | {
5 | private static readonly Dictionary s_aliasMapperMap = new(StringComparer.OrdinalIgnoreCase);
6 |
7 | static DriverJsonMapperAliases()
8 | {
9 | Register(DriverAliases.Remote);
10 | Register(DriverAliases.Chrome);
11 | Register(DriverAliases.Firefox);
12 | Register(DriverAliases.InternetExplorer);
13 | Register(DriverAliases.Safari);
14 | Register(DriverAliases.Edge);
15 | }
16 |
17 | public static void Register(string alias)
18 | where T : IDriverJsonMapper, new() =>
19 | Register(alias, new T());
20 |
21 | public static void Register(string alias, IDriverJsonMapper mapper)
22 | {
23 | alias.CheckNotNullOrWhitespace(nameof(alias));
24 | mapper.CheckNotNull(nameof(mapper));
25 |
26 | s_aliasMapperMap[alias.ToLowerInvariant()] = mapper;
27 | }
28 |
29 | public static IDriverJsonMapper Resolve(string alias) =>
30 | s_aliasMapperMap.TryGetValue(alias ?? DriverAliases.Remote, out IDriverJsonMapper mapper)
31 | ? mapper
32 | : throw new ArgumentException(
33 | $"There is no JSON mapper defined for \"{alias}\" driver alias. " +
34 | $"Use one of predefined mappers or {nameof(DriverJsonMapperAliases)}.{nameof(Register)} method to register custom driver JSON mapper.",
35 | nameof(alias));
36 | }
37 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/Chrome+NUnit.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "Chrome",
4 | "options": {
5 | "arguments": [ "disable-extensions", "start-maximized" ]
6 | },
7 | "service": {
8 | "driverPath": "{basedir}",
9 | "driverExecutableFileName": "chromedriver.exe"
10 | },
11 | "portsToIgnore": [ 60999, 60998 ]
12 | },
13 | "baseUrl": "https://demo.atata.io/",
14 | "defaultControlVisibility": "Visible",
15 | "culture": "en-US",
16 |
17 | "useNUnitTestName": true,
18 | "useNUnitTestSuiteName": true,
19 | "useNUnitTestSuiteType": true,
20 | "logConsumers": [
21 | {
22 | "type": "Atata.NUnitTestContextLogConsumer, Atata",
23 | "sectionEnd": "include"
24 | }
25 | ],
26 | "screenshots": {
27 | "strategy": {
28 | "type": "fullPageOrViewport"
29 | }
30 | },
31 | "pageSnapshots": {
32 | "fileNameTemplate": "{snapshot-number:D2}!",
33 | "strategy": {
34 | "type": "pageSource"
35 | }
36 | },
37 | "eventSubscriptions": [
38 | {
39 | "handlerType": "Atata.LogNUnitErrorEventHandler, Atata"
40 | },
41 | {
42 | "handlerType": "Atata.TakeScreenshotOnNUnitErrorEventHandler, Atata"
43 | },
44 | {
45 | "handlerType": "Atata.TakePageSnapshotOnNUnitErrorEventHandler, Atata"
46 | },
47 | {
48 | "handlerType": "Atata.AddArtifactsToNUnitTestContextEventHandler, Atata"
49 | }
50 | ],
51 |
52 | "baseRetryTimeout": 7,
53 | "baseRetryInterval": 0.7,
54 | "elementFindTimeout": 8,
55 | "elementFindRetryInterval": 0.8,
56 | "waitingTimeout": 9,
57 | "waitingRetryInterval": 0.9,
58 | "verificationTimeout": 10,
59 | "verificationRetryInterval": 1,
60 |
61 | "defaultAssemblyNamePatternToFindTypes": "def",
62 | "assemblyNamePatternToFindComponentTypes": "comp",
63 | "assemblyNamePatternToFindAttributeTypes": "attr"
64 | }
65 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Configs/Chrome.json:
--------------------------------------------------------------------------------
1 | {
2 | "driver": {
3 | "type": "chrome",
4 | "options": {
5 | "loggingPreferences": {
6 | "browser": "Info",
7 | "driver": "Warning"
8 | },
9 | "additionalOptions": {
10 | "globalcap1": true,
11 | "globalcap2": 5,
12 | "globalcap3": "str"
13 | },
14 | "additionalBrowserOptions": {
15 | "cap1": true,
16 | "cap2": 5,
17 | "cap3": "str",
18 | "cap4": {
19 | "cap4:1": false,
20 | "cap4:2": 14,
21 | "cap4:3": {
22 | "cap4:3:1": "str2"
23 | }
24 | }
25 | },
26 | "proxy": {
27 | "httpProxy": "http"
28 | },
29 | "arguments": [ "headless=new" ],
30 | "excludedArguments": [ "exc-arg" ],
31 | "encodedExtensions": [ "ZW5jLWV4dDE=", "ZW5jLWV4dDI=" ],
32 | "windowTypes": [ "win1", "win2" ],
33 | "performanceLoggingPreferences": {
34 | "isCollectingNetworkEvents": false,
35 | "IsCollectingPageEvents": false,
36 | "bufferUsageReportingInterval": "00:01:10",
37 | "tracingCategories": [ "cat1", "cat2" ]
38 | },
39 | "userProfilePreferences": {
40 | "pref1": 7,
41 | "pref2": false,
42 | "pref3": "str"
43 | },
44 | "localStatePreferences": {
45 | "pref1": 2.7,
46 | "pref2": true,
47 | "pref3": ""
48 | },
49 | "mobileEmulationDeviceName": "emul",
50 | "LeaveBrowserRunning": true,
51 | "minidumpPath": "mdp",
52 | "androidOptions": {
53 | "androidPackage": "pack1",
54 | "androidActivity": "act1"
55 | },
56 | "pageLoadTimeout": "00:00:45"
57 | },
58 | "service": {
59 | "port": 555,
60 | "hostName": "127.0.0.1",
61 | "allowedIPAddresses": "5.5.5.5,7.7.7.7"
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/DriverOptionsJsonSection.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium.Chromium;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public class DriverOptionsJsonSection : JsonSection
6 | {
7 | public string Type { get; set; }
8 |
9 | public Dictionary LoggingPreferences { get; set; }
10 |
11 | public JsonSection AdditionalOptions { get; set; }
12 |
13 | // Chrome, Firefox, Edge and InternetExplorer specific.
14 | public JsonSection AdditionalBrowserOptions { get; set; }
15 |
16 | public ProxyJsonSection Proxy { get; set; }
17 |
18 | // Chrome, Firefox and Edge specific.
19 | public string[] Arguments { get; set; }
20 |
21 | // Chrome and Edge specific.
22 | public string[] ExcludedArguments { get; set; }
23 |
24 | // Chrome and Edge specific.
25 | public string[] Extensions { get; set; }
26 |
27 | // Chrome and Edge specific.
28 | public string[] EncodedExtensions { get; set; }
29 |
30 | // Chrome and Edge specific.
31 | public string[] WindowTypes { get; set; }
32 |
33 | // Chrome and Edge specific.
34 | public DriverPerformanceLoggingPreferencesJsonSection PerformanceLoggingPreferences { get; set; }
35 |
36 | // Chrome and Edge specific.
37 | public JsonSection UserProfilePreferences { get; set; }
38 |
39 | // Chrome and Edge specific.
40 | public JsonSection LocalStatePreferences { get; set; }
41 |
42 | // Firefox specific.
43 | public DriverProfileJsonSection Profile { get; set; }
44 |
45 | // Firefox specific.
46 | public JsonSection Preferences { get; set; }
47 |
48 | // Chrome and Edge specific.
49 | public string MobileEmulationDeviceName { get; set; }
50 |
51 | // Chrome and Edge specific.
52 | public ChromiumMobileEmulationDeviceSettings MobileEmulationDeviceSettings { get; set; }
53 |
54 | // Chrome, Firefox and Edge specific.
55 | public AndroidOptionsJsonSection AndroidOptions { get; set; }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/RemoteDriverJsonMapper.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public class RemoteDriverJsonMapper : IDriverJsonMapper
6 | {
7 | public void Map(DriverJsonSection section, AtataContextBuilder builder)
8 | {
9 | RemoteDriverAtataContextBuilder driverBuilder = CreateDriverBuilder(builder);
10 |
11 | Map(section, driverBuilder);
12 | }
13 |
14 | public DriverOptions CreateOptions(DriverOptionsJsonSection section)
15 | {
16 | IDriverJsonMapper mapper = GetOptionsMapper(section.Type);
17 | return mapper.CreateOptions(section);
18 | }
19 |
20 | protected virtual RemoteDriverAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
21 | builder.UseRemoteDriver();
22 |
23 | protected virtual void Map(DriverJsonSection section, RemoteDriverAtataContextBuilder builder)
24 | {
25 | if (!string.IsNullOrWhiteSpace(section.Alias))
26 | builder.WithAlias(section.Alias);
27 |
28 | if (section.CommandTimeout != null)
29 | builder.WithCommandTimeout(TimeSpan.FromSeconds(section.CommandTimeout.Value));
30 |
31 | if (!string.IsNullOrWhiteSpace(section.RemoteAddress))
32 | builder.WithRemoteAddress(section.RemoteAddress);
33 |
34 | if (section.Options != null)
35 | builder.WithOptions(() => CreateOptions(section.Options));
36 | }
37 |
38 | private static IDriverJsonMapper GetOptionsMapper(string typeName) =>
39 | typeName?.ToLowerInvariant() switch
40 | {
41 | DriverAliases.Chrome => new ChromeDriverJsonMapper(),
42 | DriverAliases.Firefox => new FirefoxDriverJsonMapper(),
43 | DriverAliases.InternetExplorer => new InternetExplorerDriverJsonMapper(),
44 | DriverAliases.Safari => new SafariDriverJsonMapper(),
45 | DriverAliases.Edge => new EdgeDriverJsonMapper(),
46 | null => throw new ArgumentNullException(nameof(typeName), "Options type name is not defined."),
47 | _ => throw new ArgumentException($"Unsupported options type name: {typeName}.", nameof(typeName)),
48 | };
49 | }
50 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/EventSubscriptionsTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class EventSubscriptionsTests : TestFixture
4 | {
5 | [Test]
6 | public void Empty() =>
7 | AtataContext.Configure().ToSubject()
8 | .Invoking(x => x.ApplyJsonConfig(BuildConfigPath(nameof(Empty)), null))
9 | .Should.Throw();
10 |
11 | [Test]
12 | public void EventTypeAndHandlerType() =>
13 | CreateSutForConfig(nameof(EventTypeAndHandlerType))
14 | .Should.ContainSingle(x => x.EventType == typeof(TestEvent) && x.EventHandler is TestEventHandler);
15 |
16 | [Test]
17 | public void HandlerType() =>
18 | CreateSutForConfig(nameof(HandlerType))
19 | .Should.ContainSingle(x => x.EventType == typeof(TestEvent) && x.EventHandler is TestEventHandler);
20 |
21 | [Test]
22 | public void HandlerType_Invalid() =>
23 | AtataContext.Configure().ToSubject()
24 | .Invoking(x => x.ApplyJsonConfig(BuildConfigPath(nameof(HandlerType_Invalid)), null))
25 | .Should.Throw();
26 |
27 | [Test]
28 | public void EventTypeAndHandlerTypeAndArgument() =>
29 | CreateSutForConfig(nameof(EventTypeAndHandlerTypeAndArgument))
30 | .Should.ContainSingle(x => x.EventType == typeof(TestEvent) && x.EventHandler is TestEventHandler && ((TestEventHandler)x.EventHandler).SomeName == "TestName");
31 |
32 | private static Subject> CreateSutForConfig(string configName)
33 | {
34 | AtataContextBuilder builder = AtataContext.Configure().
35 | ApplyJsonConfig(BuildConfigPath(configName));
36 |
37 | return builder.BuildingContext.EventSubscriptions.ToSutSubject();
38 | }
39 |
40 | private static string BuildConfigPath(string configName) =>
41 | $"Configs/EventSubscriptions/{configName}";
42 |
43 | public class TestEvent
44 | {
45 | }
46 |
47 | public class TestEventHandler : IEventHandler
48 | {
49 | public TestEventHandler(string someName = null) =>
50 | SomeName = someName;
51 |
52 | public string SomeName { get; }
53 |
54 | public void Handle(TestEvent eventData, AtataContext context)
55 | {
56 | // Method intentionally left empty.
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Atata.Configuration.Json.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32630.192
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Atata.Configuration.Json", "src\Atata.Configuration.Json\Atata.Configuration.Json.csproj", "{D58A65C7-B2D0-4C1E-B97C-C31A6833AE35}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F6F0CC0A-EAE8-4D7F-8A87-F38178C7A84A}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | azure-pipelines.release.yml = azure-pipelines.release.yml
12 | azure-pipelines.runsettings = azure-pipelines.runsettings
13 | azure-pipelines.yml = azure-pipelines.yml
14 | CHANGELOG.md = CHANGELOG.md
15 | Directory.Build.props = Directory.Build.props
16 | README.md = README.md
17 | EndProjectSection
18 | EndProject
19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Atata.Configuration.Json.Tests", "test\Atata.Configuration.Json.Tests\Atata.Configuration.Json.Tests.csproj", "{9369C132-EBEC-4FFF-880E-AD43EACCD35A}"
20 | EndProject
21 | Global
22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
23 | Debug|Any CPU = Debug|Any CPU
24 | Release|Any CPU = Release|Any CPU
25 | EndGlobalSection
26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
27 | {D58A65C7-B2D0-4C1E-B97C-C31A6833AE35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {D58A65C7-B2D0-4C1E-B97C-C31A6833AE35}.Debug|Any CPU.Build.0 = Debug|Any CPU
29 | {D58A65C7-B2D0-4C1E-B97C-C31A6833AE35}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {D58A65C7-B2D0-4C1E-B97C-C31A6833AE35}.Release|Any CPU.Build.0 = Release|Any CPU
31 | {9369C132-EBEC-4FFF-880E-AD43EACCD35A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {9369C132-EBEC-4FFF-880E-AD43EACCD35A}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {9369C132-EBEC-4FFF-880E-AD43EACCD35A}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {9369C132-EBEC-4FFF-880E-AD43EACCD35A}.Release|Any CPU.Build.0 = Release|Any CPU
35 | EndGlobalSection
36 | GlobalSection(SolutionProperties) = preSolution
37 | HideSolutionNode = FALSE
38 | EndGlobalSection
39 | GlobalSection(ExtensibilityGlobals) = postSolution
40 | SolutionGuid = {62B7F605-0414-4B4C-8D2C-6D2AE90638B1}
41 | EndGlobalSection
42 | EndGlobal
43 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/JsonConfigManager`1.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | internal static class JsonConfigManager
4 | where TConfig : JsonConfig
5 | {
6 | private static readonly JsonSerializerSettings s_serializerSettings = new()
7 | {
8 | ContractResolver = JsonConfigContractResolver.Instance,
9 | NullValueHandling = NullValueHandling.Ignore
10 | };
11 |
12 | internal static void UpdateGlobalValue(string jsonContent, TConfig config)
13 | {
14 | PropertyInfo globalConfigProperty = GetConfigProperty(nameof(JsonConfig.Global));
15 |
16 | if (globalConfigProperty.GetValue(null, null) is TConfig currentConfig)
17 | JsonConvert.PopulateObject(jsonContent, currentConfig);
18 | else
19 | globalConfigProperty.SetValue(null, config, null);
20 | }
21 |
22 | internal static void UpdateCurrentValue(string jsonContent, TConfig config)
23 | {
24 | PropertyInfo currentConfigProperty = GetConfigProperty(nameof(JsonConfig.Current));
25 |
26 | if (currentConfigProperty.GetValue(null, null) is TConfig currentConfig)
27 | JsonConvert.PopulateObject(jsonContent, currentConfig);
28 | else
29 | currentConfigProperty.SetValue(null, config, null);
30 | }
31 |
32 | internal static void InitCurrentValue()
33 | {
34 | PropertyInfo currentConfigProperty = GetConfigProperty(nameof(JsonConfig.Current));
35 |
36 | if (currentConfigProperty.GetValue(null, null) == null)
37 | {
38 | object globalValue = GetConfigProperty(nameof(JsonConfig.Global)).GetValue(null, null);
39 |
40 | if (globalValue != null)
41 | {
42 | string serializedGlobalValue = JsonConvert.SerializeObject(globalValue, s_serializerSettings);
43 |
44 | object clonedGlobalValue = JsonConvert.DeserializeObject(serializedGlobalValue, globalValue.GetType());
45 | currentConfigProperty.SetValue(null, clonedGlobalValue, null);
46 | }
47 | }
48 | }
49 |
50 | internal static void ResetCurrentValue() =>
51 | GetConfigProperty(nameof(JsonConfig.Current)).SetValue(null, null, null);
52 |
53 | private static PropertyInfo GetConfigProperty(string name)
54 | {
55 | Type type = typeof(TConfig);
56 | PropertyInfo property = type.GetProperty(
57 | name,
58 | BindingFlags.GetProperty | BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);
59 |
60 | return property ?? throw new MissingMemberException(type.FullName, name);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/AttributeMapper.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public class AttributeMapper
4 | {
5 | private static readonly Dictionary s_alternativeParameterNamesMap = new()
6 | {
7 | ["value"] = "values",
8 | ["case"] = "termCase"
9 | };
10 |
11 | private readonly Assembly[] _assembliesToFindAttributeTypes;
12 |
13 | private readonly ObjectCreator _objectCreator;
14 |
15 | public AttributeMapper(string assemblyNamePatternToFindAttributeTypes, string defaultAssemblyNamePatternToFindTypes)
16 | {
17 | _assembliesToFindAttributeTypes = AssemblyFinder.FindAllByPattern(assemblyNamePatternToFindAttributeTypes);
18 |
19 | ObjectConverter objectConverter = new()
20 | {
21 | AssemblyNamePatternToFindTypes = defaultAssemblyNamePatternToFindTypes
22 | };
23 | ObjectMapper objectMapper = new(objectConverter);
24 | _objectCreator = new(objectConverter, objectMapper);
25 | }
26 |
27 | public Attribute Map(AttributeJsonSection section)
28 | {
29 | if (string.IsNullOrEmpty(section.Type))
30 | throw new ConfigurationException(
31 | "\"type\" configuration property of attribute section is not specified.");
32 |
33 | string typeName = NormalizeAttributeTypeName(section.Type);
34 |
35 | Type attributeType = TypeFinder.FindInAssemblies(typeName, _assembliesToFindAttributeTypes);
36 |
37 | if (!typeof(Attribute).IsAssignableFrom(attributeType))
38 | throw new ConfigurationException(
39 | $"\"type\"=\"{section.Type}\" configuration property of attribute section doesn't reference an attribute type.");
40 |
41 | var valuesMap = section.ExtraPropertiesMap.ToDictionary(
42 | x => x.Key,
43 | x => PostProcessConfigurationValue(x.Key, x.Value));
44 |
45 | return (Attribute)_objectCreator.Create(attributeType, valuesMap, s_alternativeParameterNamesMap);
46 | }
47 |
48 | private static object PostProcessConfigurationValue(string name, object value)
49 | {
50 | if (name.EndsWith("AttributeType", StringComparison.Ordinal) && value is string stringValue)
51 | return NormalizeAttributeTypeName(stringValue);
52 |
53 | if (name.EndsWith("AttributeTypes", StringComparison.Ordinal) && value is object[] arrayValue)
54 | return arrayValue.Select(x => NormalizeAttributeTypeName(x.ToString())).ToArray();
55 |
56 | return value;
57 | }
58 |
59 | private static string NormalizeAttributeTypeName(string typeName) =>
60 | !typeName.Contains(",") && !typeName.EndsWith(nameof(Attribute), StringComparison.OrdinalIgnoreCase)
61 | ? typeName + nameof(Attribute)
62 | : typeName;
63 | }
64 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/LogConsumerTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | [TestFixture]
4 | public class LogConsumerTests : TestFixture
5 | {
6 | [Test]
7 | public void Multiple_ViaSingleConfig()
8 | {
9 | AtataContextBuilder builder = AtataContext.Configure()
10 | .ApplyJsonConfig("Configs/LogConsumers");
11 |
12 | LogConsumerConfiguration[] expected =
13 | [
14 | new LogConsumerConfiguration(new DebugLogConsumer { Separator = " - " }),
15 | new LogConsumerConfiguration(new TraceLogConsumer(), LogLevel.Trace)
16 | {
17 | MessageNestingLevelIndent = "_ ",
18 | MessageStartSectionPrefix = "S:",
19 | MessageEndSectionPrefix = "E:",
20 | },
21 | new LogConsumerConfiguration(new NUnitTestContextLogConsumer(), LogLevel.Info, LogSectionEndOption.Exclude),
22 | new LogConsumerConfiguration(new NLogConsumer { LoggerName = "somelogger" }, LogLevel.Warn, LogSectionEndOption.IncludeForBlocks),
23 | new LogConsumerConfiguration(new CustomLogConsumer { IntProperty = 15 }, LogLevel.Error)
24 | ];
25 |
26 | AssertLogConsumers(expected, builder.BuildingContext.LogConsumerConfigurations);
27 |
28 | JsonConfig.Current.LogConsumers.Count.Should().Be(expected.Length);
29 | }
30 |
31 | [Test]
32 | public void Multiple_ViaMultipleConfigs()
33 | {
34 | AtataContextBuilder builder = AtataContext.Configure()
35 | .ApplyJsonConfig("Configs/DebugLogConsumers")
36 | .ApplyJsonConfig("Configs/TraceLogConsumers");
37 |
38 | LogConsumerConfiguration[] expected =
39 | [
40 | new LogConsumerConfiguration(new DebugLogConsumer { Separator = " - " }),
41 | new LogConsumerConfiguration(new TraceLogConsumer(), LogLevel.Trace)
42 | ];
43 |
44 | AssertLogConsumers(expected, builder.BuildingContext.LogConsumerConfigurations);
45 |
46 | JsonConfig.Current.LogConsumers.Select(x => x.Type)
47 | .Should().Equal(LogConsumerAliases.Debug, LogConsumerAliases.Trace);
48 | }
49 |
50 | private static void AssertLogConsumers(IEnumerable expected, IEnumerable actual) =>
51 | actual.Should().BeEquivalentTo(
52 | expected,
53 | opt => opt.IncludingAllRuntimeProperties().Using(ctx =>
54 | {
55 | ctx.Subject.Should().BeOfType(ctx.Expectation.GetType());
56 | ctx.Subject.Should().BeEquivalentTo(ctx.Expectation, opt2 => opt2.IncludingAllRuntimeProperties());
57 | }).WhenTypeIs());
58 |
59 | public class CustomLogConsumer : ILogConsumer
60 | {
61 | public int? IntProperty { get; set; }
62 |
63 | public void Log(LogEventInfo eventInfo) =>
64 | throw new NotSupportedException();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/ChromiumDriverJsonMapper`3.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium.Chromium;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public abstract class ChromiumDriverJsonMapper : DriverJsonMapper
6 | where TBuilder : DriverAtataContextBuilder
7 | where TService : ChromiumDriverService
8 | where TOptions : ChromiumOptions, new()
9 | {
10 | protected override void MapOptions(DriverOptionsJsonSection section, TOptions options)
11 | {
12 | base.MapOptions(section, options);
13 |
14 | if (section.Arguments is { Length: > 0 })
15 | options.AddArguments(section.Arguments);
16 |
17 | if (section.ExcludedArguments is { Length: > 0 })
18 | options.AddExcludedArguments(section.ExcludedArguments);
19 |
20 | if (section.Extensions is { Length: > 0 })
21 | options.AddExtensions(section.Extensions);
22 |
23 | if (section.EncodedExtensions is { Length: > 0 })
24 | options.AddEncodedExtensions(section.EncodedExtensions);
25 |
26 | if (section.WindowTypes is { Length: > 0 })
27 | options.AddWindowTypes(section.WindowTypes);
28 |
29 | if (section.PerformanceLoggingPreferences != null)
30 | {
31 | options.PerformanceLoggingPreferences = new ChromiumPerformanceLoggingPreferences();
32 | MapPerformanceLoggingPreferences(section.PerformanceLoggingPreferences, options.PerformanceLoggingPreferences);
33 | }
34 |
35 | if (section.UserProfilePreferences != null)
36 | {
37 | foreach (var item in section.UserProfilePreferences.ExtraPropertiesMap)
38 | options.AddUserProfilePreference(item.Key, FillTemplateVariables(item.Value));
39 | }
40 |
41 | if (section.LocalStatePreferences != null)
42 | {
43 | foreach (var item in section.LocalStatePreferences.ExtraPropertiesMap)
44 | options.AddLocalStatePreference(item.Key, FillTemplateVariables(item.Value));
45 | }
46 |
47 | if (!string.IsNullOrWhiteSpace(section.MobileEmulationDeviceName))
48 | options.EnableMobileEmulation(section.MobileEmulationDeviceName);
49 |
50 | if (section.MobileEmulationDeviceSettings != null)
51 | options.EnableMobileEmulation(section.MobileEmulationDeviceSettings);
52 |
53 | if (section.AndroidOptions != null)
54 | options.AndroidOptions = CreateAndMapAndroidOptions(section.AndroidOptions);
55 | }
56 |
57 | private void MapPerformanceLoggingPreferences(DriverPerformanceLoggingPreferencesJsonSection section, ChromiumPerformanceLoggingPreferences preferences)
58 | {
59 | ObjectMapper.Map(section.ExtraPropertiesMap, preferences);
60 |
61 | if (section.TracingCategories is { Length: > 0 })
62 | preferences.AddTracingCategories(section.TracingCategories);
63 | }
64 |
65 | private ChromiumAndroidOptions CreateAndMapAndroidOptions(AndroidOptionsJsonSection section)
66 | {
67 | if (string.IsNullOrEmpty(section.AndroidPackage))
68 | throw new ConfigurationException(
69 | "\"androidPackage\" configuration property of \"androidOptions\" section is not specified.");
70 |
71 | var androidOptions = new ChromiumAndroidOptions(FillTemplateVariables(section.AndroidPackage));
72 | ObjectMapper.Map(section.ExtraPropertiesMap, androidOptions);
73 |
74 | return androidOptions;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/ConfigFilePathTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | [TestFixture]
4 | public class ConfigFilePathTests : TestFixture
5 | {
6 | [Test]
7 | public void Default()
8 | {
9 | AtataContextBuilder builder = AtataContext.Configure()
10 | .ApplyJsonConfig();
11 |
12 | builder.BuildingContext.BaseUrl.Should().EndWith("atata");
13 | }
14 |
15 | [Test]
16 | public void Default_WithAlias()
17 | {
18 | AtataContextBuilder builder = AtataContext.Configure()
19 | .ApplyJsonConfig(environmentAlias: "QA");
20 |
21 | builder.BuildingContext.BaseUrl.Should().EndWith("atata.qa");
22 | }
23 |
24 | [Test]
25 | public void FileName()
26 | {
27 | string filePath = "Simple.json";
28 |
29 | AtataContextBuilder builder = AtataContext.Configure()
30 | .ApplyJsonConfig(filePath);
31 |
32 | builder.BuildingContext.BaseUrl.Should().EndWith("simple");
33 | }
34 |
35 | [Test]
36 | public void FileName_WithAlias()
37 | {
38 | string filePath = "Simple.json";
39 |
40 | AtataContextBuilder builder = AtataContext.Configure()
41 | .ApplyJsonConfig(filePath, "QA");
42 |
43 | builder.BuildingContext.BaseUrl.Should().EndWith("simple.qa");
44 | }
45 |
46 | [Test]
47 | public void FileName_WithoutExtension()
48 | {
49 | string filePath = "Simple";
50 |
51 | AtataContextBuilder builder = AtataContext.Configure()
52 | .ApplyJsonConfig(filePath);
53 |
54 | builder.BuildingContext.BaseUrl.Should().EndWith("simple");
55 | }
56 |
57 | [Test]
58 | public void FileName_WithAlias_WithoutExtension()
59 | {
60 | string filePath = "Simple";
61 |
62 | AtataContextBuilder builder = AtataContext.Configure()
63 | .ApplyJsonConfig(filePath, "QA");
64 |
65 | builder.BuildingContext.BaseUrl.Should().EndWith("simple.qa");
66 | }
67 |
68 | [Test]
69 | public void AbsolutePath()
70 | {
71 | string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Simple.json");
72 |
73 | AtataContextBuilder builder = AtataContext.Configure()
74 | .ApplyJsonConfig(filePath);
75 |
76 | builder.BuildingContext.BaseUrl.Should().EndWith("simple");
77 | }
78 |
79 | [Test]
80 | public void AbsolutePath_WithAlias()
81 | {
82 | string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Simple.json");
83 |
84 | AtataContextBuilder builder = AtataContext.Configure()
85 | .ApplyJsonConfig(filePath, "QA");
86 |
87 | builder.BuildingContext.BaseUrl.Should().EndWith("simple.qa");
88 | }
89 |
90 | [Test]
91 | public void DirectoryPath()
92 | {
93 | string filePath = AppDomain.CurrentDomain.BaseDirectory;
94 |
95 | AtataContextBuilder builder = AtataContext.Configure()
96 | .ApplyJsonConfig(filePath);
97 |
98 | builder.BuildingContext.BaseUrl.Should().EndWith("atata");
99 | }
100 |
101 | [Test]
102 | public void DirectoryPath_WithAlias()
103 | {
104 | string filePath = AppDomain.CurrentDomain.BaseDirectory;
105 |
106 | AtataContextBuilder builder = AtataContext.Configure()
107 | .ApplyJsonConfig(filePath, "QA");
108 |
109 | builder.BuildingContext.BaseUrl.Should().EndWith("atata.qa");
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/EventSubscriptionMapper.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public sealed class EventSubscriptionMapper
4 | {
5 | private readonly Assembly[] _assembliesToFindEventTypes;
6 |
7 | private readonly Assembly[] _assembliesToFindEventHandlerTypes;
8 |
9 | private readonly ObjectCreator _objectCreator;
10 |
11 | public EventSubscriptionMapper(
12 | string assemblyNamePatternToFindEventTypes,
13 | string assemblyNamePatternToFindEventHandlerTypes,
14 | string defaultAssemblyNamePatternToFindTypes)
15 | {
16 | _assembliesToFindEventTypes = AssemblyFinder.FindAllByPattern(assemblyNamePatternToFindEventTypes);
17 | _assembliesToFindEventHandlerTypes = AssemblyFinder.FindAllByPattern(assemblyNamePatternToFindEventHandlerTypes);
18 |
19 | IObjectConverter objectConverter = new ObjectConverter
20 | {
21 | AssemblyNamePatternToFindTypes = defaultAssemblyNamePatternToFindTypes
22 | };
23 | IObjectMapper objectMapper = new ObjectMapper(objectConverter);
24 | _objectCreator = new ObjectCreator(objectConverter, objectMapper);
25 | }
26 |
27 | public EventSubscriptionItem Map(EventSubscriptionJsonSection section)
28 | {
29 | if (string.IsNullOrEmpty(section.HandlerType))
30 | throw new ConfigurationException(
31 | $"\"{nameof(EventSubscriptionJsonSection.HandlerType)}\" configuration property of event subscription section is not specified.");
32 |
33 | Type handlerType = TypeFinder.FindInAssemblies(section.HandlerType, _assembliesToFindEventHandlerTypes);
34 |
35 | if (string.IsNullOrEmpty(section.EventType))
36 | {
37 | return CreateSubscription(handlerType, section.ExtraPropertiesMap);
38 | }
39 | else
40 | {
41 | Type eventType = TypeFinder.FindInAssemblies(section.EventType, _assembliesToFindEventTypes);
42 | return CreateSubscription(eventType, handlerType, section.ExtraPropertiesMap);
43 | }
44 | }
45 |
46 | private EventSubscriptionItem CreateSubscription(Type eventType, Type eventHandlerType, Dictionary eventHandlerValuesMap)
47 | {
48 | Type expectedType = typeof(IEventHandler<>).MakeGenericType(eventType);
49 |
50 | if (!expectedType.IsAssignableFrom(eventHandlerType))
51 | throw new ConfigurationException(
52 | $"\"{nameof(EventSubscriptionJsonSection.HandlerType)}\" configuration property of {eventHandlerType.FullName} type doesn't implement {expectedType.FullName}.");
53 |
54 | object eventHandler = _objectCreator.Create(eventHandlerType, eventHandlerValuesMap);
55 |
56 | return new EventSubscriptionItem(eventType, eventHandler);
57 | }
58 |
59 | private EventSubscriptionItem CreateSubscription(Type eventHandlerType, Dictionary eventHandlerValuesMap)
60 | {
61 | Type expectedInterfaceType = typeof(IEventHandler<>);
62 |
63 | Type eventHanderType = eventHandlerType.GetGenericInterfaceType(expectedInterfaceType)
64 | ?? throw new ConfigurationException(
65 | $"\"{nameof(EventSubscriptionJsonSection.HandlerType)}\" configuration property of {eventHandlerType.FullName} type doesn't implement {expectedInterfaceType.FullName}.");
66 |
67 | Type eventType = eventHanderType.GetGenericArguments()[0];
68 |
69 | object eventHandler = _objectCreator.Create(eventHandlerType, eventHandlerValuesMap);
70 |
71 | return new EventSubscriptionItem(eventType, eventHandler);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at yevgeniy.shunevych@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/AttributeTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | public class AttributeTests : TestFixture
4 | {
5 | [Test]
6 | public void Global()
7 | {
8 | AtataContextBuilder builder = AtataContext.Configure()
9 | .ApplyJsonConfig("Configs/Attributes/Global");
10 |
11 | var result = builder.BuildingContext.Attributes.Global;
12 |
13 | result.Should().HaveCount(4);
14 |
15 | var attribute1 = result[0].Should().BeOfType().Subject;
16 | attribute1.Case.Should().Be(TermCase.LowerMerged);
17 | attribute1.TargetTypes.Should().Equal(typeof(Field<,>));
18 | attribute1.TargetAttributeTypes.Should().Equal(typeof(FindByIdAttribute));
19 | attribute1.ExcludeTargetTypes.Should().Equal(typeof(CheckBox<>));
20 |
21 | var attribute2 = result[1].Should().BeOfType().Subject;
22 | attribute2.Visibility.Should().Be(Visibility.Any);
23 | attribute2.TargetTypes.Should().Equal(typeof(Table<,>), typeof(Table<,,>));
24 | attribute2.TargetAttributeTypes.Should().Equal(typeof(FindByClassAttribute), typeof(FindByFieldSetAttribute), typeof(FindByLabelAttribute));
25 | attribute2.ExcludeTargetNames.Should().Equal("a", "b");
26 | attribute2.ExcludeTargetParentTypes.Should().Equal(typeof(Frame<>));
27 |
28 | var attribute3 = result[2].Should().BeOfType().Subject;
29 | attribute3.Values.Should().Equal("some-id");
30 |
31 | var attribute4 = result[3].Should().BeOfType().Subject;
32 | attribute4.Values.Should().Equal("name1", "name2");
33 | }
34 |
35 | [Test]
36 | public void Assembly()
37 | {
38 | AtataContextBuilder builder = AtataContext.Configure()
39 | .ApplyJsonConfig("Configs/Attributes/Assembly");
40 |
41 | var result = builder.BuildingContext.Attributes.AssemblyMap;
42 |
43 | result.Should().HaveCount(2);
44 |
45 | var assembly1 = System.Reflection.Assembly.GetAssembly(GetType());
46 |
47 | result[assembly1].Should().ContainSingle()
48 | .Which.Should().BeOfType()
49 | .Which.Values.Should().Equal("some-id");
50 |
51 | var assembly2 = System.Reflection.Assembly.GetAssembly(typeof(AtataContext));
52 |
53 | result[assembly2].Should().ContainSingle()
54 | .Which.Should().BeOfType()
55 | .Which.Values.Should().Equal("some-name");
56 | }
57 |
58 | [Test]
59 | public void Component()
60 | {
61 | AtataContextBuilder builder = AtataContext.Configure()
62 | .ApplyJsonConfig("Configs/Attributes/Component");
63 |
64 | var result = builder.BuildingContext.Attributes.ComponentMap;
65 |
66 | result.Should().HaveCount(1);
67 |
68 | result[typeof(OrdinaryPage)].Should().ContainSingle()
69 | .Which.Should().BeOfType()
70 | .Which.Value.Should().Be("/some-url");
71 | }
72 |
73 | [Test]
74 | public void Property()
75 | {
76 | AtataContextBuilder builder = AtataContext.Configure()
77 | .ApplyJsonConfig("Configs/Attributes/Property");
78 |
79 | var result = builder.BuildingContext.Attributes.PropertyMap;
80 |
81 | result.Should().HaveCount(2);
82 |
83 | result[new TypePropertyNamePair(typeof(OrdinaryPage), "Prop1")].Should().ContainSingle()
84 | .Which.Should().BeOfType()
85 | .Which.Values.Should().Equal("some-id");
86 |
87 | result[new TypePropertyNamePair(typeof(OrdinaryPage), "Prop2")].Should().ContainSingle()
88 | .Which.Should().BeOfType()
89 | .Which.Values.Should().Equal("some-name");
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/GeneralSettingsTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | [TestFixture]
4 | public class GeneralSettingsTests : TestFixture
5 | {
6 | [Test]
7 | public void GeneralAndNUnit()
8 | {
9 | AtataContextBuilder builder = AtataContext.Configure()
10 | .ApplyJsonConfig("Configs/Chrome+NUnit.json");
11 |
12 | var context = builder.BuildingContext;
13 |
14 | using (new AssertionScope())
15 | {
16 | context.BaseUrl.Should().Be("https://demo.atata.io/");
17 |
18 | context.DefaultControlVisibility.Should().Be(Visibility.Visible);
19 |
20 | context.Culture.Name.Should().Be("en-US");
21 |
22 | context.EventSubscriptions.Where(x => x.EventType == typeof(AtataContextDeInitEvent))
23 | .Should().HaveCount(3);
24 | context.EventSubscriptions.Where(x => x.EventType == typeof(AtataContextDeInitCompletedEvent))
25 | .Should().HaveCount(2);
26 | context.EventSubscriptions.Should().Contain(x => x.EventHandler is LogNUnitErrorEventHandler);
27 | context.EventSubscriptions.Should().Contain(x => x.EventHandler is TakeScreenshotOnNUnitErrorEventHandler);
28 | context.EventSubscriptions.Should().Contain(x => x.EventHandler is TakePageSnapshotOnNUnitErrorEventHandler);
29 | context.EventSubscriptions.Should().Contain(x => x.EventHandler is AddArtifactsToNUnitTestContextEventHandler);
30 |
31 | context.BaseRetryTimeout.Should().Be(TimeSpan.FromSeconds(7));
32 | context.BaseRetryInterval.Should().Be(TimeSpan.FromSeconds(0.7));
33 |
34 | context.ElementFindTimeout.Should().Be(TimeSpan.FromSeconds(8));
35 | context.ElementFindRetryInterval.Should().Be(TimeSpan.FromSeconds(0.8));
36 |
37 | context.WaitingTimeout.Should().Be(TimeSpan.FromSeconds(9));
38 | context.WaitingRetryInterval.Should().Be(TimeSpan.FromSeconds(0.9));
39 |
40 | context.VerificationTimeout.Should().Be(TimeSpan.FromSeconds(10));
41 | context.VerificationRetryInterval.Should().Be(TimeSpan.FromSeconds(1));
42 |
43 | context.TestNameFactory().Should().Be(nameof(GeneralAndNUnit));
44 | context.TestSuiteNameFactory().Should().Be(nameof(GeneralSettingsTests));
45 | context.TestSuiteTypeFactory().Should().Be(typeof(GeneralSettingsTests));
46 |
47 | context.DefaultAssemblyNamePatternToFindTypes.Should().Be("def");
48 | context.AssemblyNamePatternToFindComponentTypes.Should().Be("comp");
49 | context.AssemblyNamePatternToFindAttributeTypes.Should().Be("attr");
50 |
51 | context.Screenshots.Strategy.Should().BeOfType();
52 |
53 | context.PageSnapshots.FileNameTemplate.Should().Be("{snapshot-number:D2}!");
54 | context.PageSnapshots.Strategy.Should().BeOfType();
55 | }
56 | }
57 |
58 | [Test]
59 | public void VerificationProperties()
60 | {
61 | AtataContextBuilder builder = AtataContext.Configure()
62 | .ApplyJsonConfig("Configs/VerificationProperties.json");
63 |
64 | var context = builder.BuildingContext;
65 |
66 | using (new AssertionScope())
67 | {
68 | context.AssertionExceptionType.Should().Be(typeof(NUnit.Framework.AssertionException));
69 | context.AggregateAssertionExceptionType.Should().Be(typeof(MultipleAssertException));
70 | context.AggregateAssertionStrategy.Should().BeOfType();
71 | context.WarningReportStrategy.Should().BeOfType();
72 | context.AssertionFailureReportStrategy.Should().BeOfType();
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Extensions/JsonAtataContextBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Atata.Configuration.Json;
2 |
3 | namespace Atata;
4 |
5 | ///
6 | /// Provides a set of extension methods for configuration through JSON config files.
7 | ///
8 | public static class JsonAtataContextBuilderExtensions
9 | {
10 | ///
11 | /// Applies JSON configuration from the file. By default reads "Atata.json" file.
12 | ///
13 | /// The builder.
14 | /// The file path.
15 | /// The environment alias.
16 | /// The instance.
17 | public static AtataContextBuilder ApplyJsonConfig(this AtataContextBuilder builder, string filePath = null, string environmentAlias = null) =>
18 | builder.ApplyJsonConfig(filePath, environmentAlias);
19 |
20 | ///
21 | /// Applies JSON configuration from the file. By default reads "Atata.json" file.
22 | ///
23 | /// The type of the configuration class.
24 | /// The builder.
25 | /// The file path.
26 | /// The environment alias.
27 | /// The instance.
28 | public static AtataContextBuilder ApplyJsonConfig(this AtataContextBuilder builder, string filePath = null, string environmentAlias = null)
29 | where TConfig : JsonConfig, new()
30 | {
31 | string jsonContent = JsonConfigFile.ReadText(filePath, environmentAlias);
32 |
33 | TConfig config = JsonConvert.DeserializeObject(jsonContent);
34 |
35 | AtataContextBuilder resultBuilder = JsonConfigMapper.Map(config, builder);
36 |
37 | if (builder.BuildingContext == AtataContext.GlobalConfiguration.BuildingContext)
38 | {
39 | JsonConfigManager.UpdateGlobalValue(jsonContent, config);
40 |
41 | EnsureInitConfigEventHandlerIsSubscribed(resultBuilder);
42 | }
43 | else
44 | {
45 | JsonConfigManager.InitCurrentValue();
46 | JsonConfigManager.UpdateCurrentValue(jsonContent, config);
47 | }
48 |
49 | EnsureResetConfigEventHandlerIsSubscribed(resultBuilder);
50 |
51 | resultBuilder.EventSubscriptions.Add(
52 | new LogJsonConfigPathEventHandler(JsonConfigFile.GetRelativePath(filePath, environmentAlias)));
53 |
54 | return resultBuilder;
55 | }
56 |
57 | ///
58 | /// Applies JSON configuration from parameter.
59 | ///
60 | /// The type of the configuration class.
61 | /// The builder.
62 | /// The configuration.
63 | /// The instance.
64 | public static AtataContextBuilder ApplyJsonConfig(this AtataContextBuilder builder, JsonConfig config)
65 | where TConfig : JsonConfig =>
66 | JsonConfigMapper.Map((TConfig)config, builder);
67 |
68 | private static void EnsureInitConfigEventHandlerIsSubscribed(AtataContextBuilder builder)
69 | where TConfig : JsonConfig
70 | {
71 | bool isInitConfigSubscribed = builder.BuildingContext.EventSubscriptions
72 | .Exists(x => x.EventType == typeof(AtataContextInitStartedEvent) && x.EventHandler is InitCurrentJsonConfigEventHandler);
73 |
74 | if (!isInitConfigSubscribed)
75 | builder.EventSubscriptions.Add(new InitCurrentJsonConfigEventHandler());
76 | }
77 |
78 | private static void EnsureResetConfigEventHandlerIsSubscribed(AtataContextBuilder builder)
79 | where TConfig : JsonConfig
80 | {
81 | bool isResetConfigSubscribed = builder.BuildingContext.EventSubscriptions
82 | .Exists(x => x.EventType == typeof(AtataContextDeInitCompletedEvent) && x.EventHandler is ResetCurrentJsonConfigEventHandler);
83 |
84 | if (!isResetConfigSubscribed)
85 | builder.EventSubscriptions.Add(new ResetCurrentJsonConfigEventHandler());
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/DriverJsonMapper`3.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public abstract class DriverJsonMapper : IDriverJsonMapper
6 | where TBuilder : DriverAtataContextBuilder
7 | where TService : DriverService
8 | where TOptions : DriverOptions, new()
9 | {
10 | public const string BaseDirectoryVariable = "{basedir}";
11 |
12 | private readonly Lazy _lazyObjectMapper = new(
13 | () => new ObjectMapper(new ObjectConverter()));
14 |
15 | protected IObjectMapper ObjectMapper => _lazyObjectMapper.Value;
16 |
17 | public void Map(DriverJsonSection section, AtataContextBuilder builder)
18 | {
19 | TBuilder driverBuilder = CreateDriverBuilder(builder);
20 |
21 | Map(section, driverBuilder);
22 | }
23 |
24 | public DriverOptions CreateOptions(DriverOptionsJsonSection section)
25 | {
26 | TOptions options = new TOptions();
27 |
28 | MapOptions(section, options);
29 |
30 | return options;
31 | }
32 |
33 | protected abstract TBuilder CreateDriverBuilder(AtataContextBuilder builder);
34 |
35 | protected virtual void Map(DriverJsonSection section, TBuilder builder)
36 | {
37 | if (!string.IsNullOrWhiteSpace(section.Alias))
38 | builder.WithAlias(section.Alias);
39 |
40 | if (section.CreateRetries != null)
41 | builder.WithCreateRetries(section.CreateRetries.Value);
42 |
43 | if (section.InitialHealthCheck != null)
44 | builder.WithInitialHealthCheck(section.InitialHealthCheck.Value);
45 |
46 | if (section.CommandTimeout != null)
47 | builder.WithCommandTimeout(TimeSpan.FromSeconds(section.CommandTimeout.Value));
48 |
49 | if (section.PortsToIgnore is { Length: > 0 })
50 | builder.WithPortsToIgnore(section.PortsToIgnore);
51 |
52 | if (!string.IsNullOrWhiteSpace(section.Service?.DriverPath))
53 | builder.WithDriverPath(FormatDriverPath(section.Service.DriverPath));
54 |
55 | if (!string.IsNullOrWhiteSpace(section.Service?.DriverExecutableFileName))
56 | builder.WithDriverExecutableFileName(section.Service.DriverExecutableFileName);
57 |
58 | if (section.Options != null)
59 | builder.WithOptions(opt => MapOptions(section.Options, opt));
60 |
61 | if (section.Service != null)
62 | builder.WithDriverService(srv => MapService(section.Service, srv));
63 | }
64 |
65 | protected virtual void MapOptions(DriverOptionsJsonSection section, TOptions options)
66 | {
67 | var properties = section.ExtraPropertiesMap;
68 |
69 | if (properties is { Count: > 0 })
70 | ObjectMapper.Map(properties, options);
71 |
72 | if (section.Proxy != null)
73 | {
74 | options.Proxy = new Proxy();
75 | MapProxy(section.Proxy, options.Proxy);
76 | }
77 |
78 | if (section.AdditionalOptions != null)
79 | {
80 | foreach (var item in section.AdditionalOptions.ExtraPropertiesMap)
81 | options.AddAdditionalOption(item.Key, FillTemplateVariables(item.Value));
82 | }
83 |
84 | if (section.LoggingPreferences is { Count: > 0 })
85 | {
86 | foreach (var item in section.LoggingPreferences)
87 | options.SetLoggingPreference(item.Key, item.Value);
88 | }
89 | }
90 |
91 | private void MapProxy(ProxyJsonSection section, Proxy proxy)
92 | {
93 | ObjectMapper.Map(section.ExtraPropertiesMap, proxy);
94 |
95 | if (section.BypassAddresses is { Length: > 0 })
96 | proxy.AddBypassAddresses(section.BypassAddresses);
97 | }
98 |
99 | protected virtual void MapService(DriverServiceJsonSection section, TService service)
100 | {
101 | var properties = section.ExtraPropertiesMap;
102 |
103 | if (properties is { Count: > 0 })
104 | ObjectMapper.Map(properties, service);
105 | }
106 |
107 | private static string FormatDriverPath(string driverPath) =>
108 | driverPath.Contains(BaseDirectoryVariable)
109 | ? driverPath.Replace(BaseDirectoryVariable, AppDomain.CurrentDomain.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
110 | : driverPath;
111 |
112 | protected static object FillTemplateVariables(object value) =>
113 | value is string valueAsString
114 | ? FillTemplateVariables(valueAsString)
115 | : value;
116 |
117 | protected static string FillTemplateVariables(string value) =>
118 | AtataContext.Current?.FillTemplateString(value) ?? value;
119 | }
120 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Extensions/CheckExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Atata;
2 |
3 | internal static class CheckExtensions
4 | {
5 | internal static T Check(this T value, Predicate checkPredicate, string argumentName, string errorMessage)
6 | {
7 | if (checkPredicate is not null && !checkPredicate(value))
8 | throw new ArgumentException(errorMessage, argumentName);
9 |
10 | return value;
11 | }
12 |
13 | internal static T CheckNotNull(this T value, string argumentName, string errorMessage = null)
14 | {
15 | if (value is null)
16 | throw CreateArgumentNullException(argumentName, errorMessage);
17 |
18 | return value;
19 | }
20 |
21 | internal static string CheckNotNullOrWhitespace(this string value, string argumentName, string errorMessage = null)
22 | {
23 | if (value is null)
24 | throw CreateArgumentNullException(argumentName, errorMessage);
25 | if (string.IsNullOrWhiteSpace(value))
26 | throw new ArgumentException(ConcatMessage("Should not be empty string or whitespace.", errorMessage), argumentName);
27 |
28 | return value;
29 | }
30 |
31 | internal static string CheckNotNullOrEmpty(this string value, string argumentName, string errorMessage = null)
32 | {
33 | if (value is null)
34 | throw CreateArgumentNullException(argumentName, errorMessage);
35 | if (value.Length == 0)
36 | throw new ArgumentException(ConcatMessage("Should not be empty string.", errorMessage), argumentName);
37 |
38 | return value;
39 | }
40 |
41 | internal static IEnumerable CheckNotNullOrEmpty(this IEnumerable collection, string argumentName, string errorMessage = null)
42 | {
43 | if (collection is null)
44 | throw CreateArgumentNullException(argumentName, errorMessage);
45 |
46 | bool isEmpty = collection is IReadOnlyCollection collectionCasted
47 | ? collectionCasted.Count == 0
48 | : !collection.Any();
49 |
50 | if (isEmpty)
51 | throw new ArgumentException(ConcatMessage("Collection should contain at least one element.", errorMessage), argumentName);
52 |
53 | return collection;
54 | }
55 |
56 | internal static T CheckNotEquals(this T value, string argumentName, T invalidValue, string errorMessage = null)
57 | where T : struct
58 | {
59 | if (Equals(value, invalidValue))
60 | throw new ArgumentException(ConcatMessage($"Invalid {typeof(T).FullName} value: {value}. Should not equal to: {invalidValue}.", errorMessage), argumentName);
61 |
62 | return value;
63 | }
64 |
65 | internal static T CheckGreaterOrEqual(this T value, string argumentName, T checkValue, string errorMessage = null)
66 | where T : struct, IComparable
67 | {
68 | if (value.CompareTo(checkValue) < 0)
69 | throw new ArgumentOutOfRangeException(argumentName, value, ConcatMessage($"Invalid {typeof(T).FullName} value: {value}. Should be greater or equal to: {checkValue}.", errorMessage));
70 |
71 | return value;
72 | }
73 |
74 | internal static T CheckLessOrEqual(this T value, string argumentName, T checkValue, string errorMessage = null)
75 | where T : struct, IComparable
76 | {
77 | if (value.CompareTo(checkValue) > 0)
78 | throw new ArgumentOutOfRangeException(argumentName, value, ConcatMessage($"Invalid {typeof(T).FullName} value: {value}. Should be less or equal to: {checkValue}.", errorMessage));
79 |
80 | return value;
81 | }
82 |
83 | internal static int CheckIndexNonNegative(this int index)
84 | {
85 | if (index < 0)
86 | throw new ArgumentOutOfRangeException(nameof(index), index, "Index was out of range. Must be non-negative.");
87 |
88 | return index;
89 | }
90 |
91 | internal static Type CheckIs(this Type value, string argumentName, string errorMessage = null)
92 | {
93 | value.CheckNotNull(argumentName);
94 |
95 | Type expectedType = typeof(T);
96 |
97 | if (!expectedType.IsAssignableFrom(value))
98 | throw new ArgumentException(ConcatMessage($"{value.FullName} type should be assignable to {expectedType.FullName}.", errorMessage), argumentName);
99 |
100 | return value;
101 | }
102 |
103 | private static ArgumentNullException CreateArgumentNullException(string argumentName, string message) =>
104 | message is null
105 | ? new ArgumentNullException(argumentName)
106 | : new ArgumentNullException(argumentName, message);
107 |
108 | private static string ConcatMessage(string primaryMessage, string secondaryMessage) =>
109 | string.IsNullOrEmpty(secondaryMessage)
110 | ? primaryMessage
111 | : $"{primaryMessage} {secondaryMessage}";
112 | }
113 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/FirefoxDriverJsonMapper.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium.Firefox;
2 |
3 | namespace Atata.Configuration.Json;
4 |
5 | public class FirefoxDriverJsonMapper : DriverJsonMapper
6 | {
7 | protected override FirefoxAtataContextBuilder CreateDriverBuilder(AtataContextBuilder builder) =>
8 | builder.UseFirefox();
9 |
10 | protected override void MapOptions(DriverOptionsJsonSection section, FirefoxOptions options)
11 | {
12 | base.MapOptions(section, options);
13 |
14 | if (section.AdditionalBrowserOptions != null)
15 | {
16 | foreach (var item in section.AdditionalBrowserOptions.ExtraPropertiesMap)
17 | options.AddAdditionalFirefoxOption(item.Key, FillTemplateVariables(item.Value));
18 | }
19 |
20 | if (section.Arguments is { Length: > 0 })
21 | options.AddArguments(section.Arguments);
22 |
23 | if (section.Profile != null)
24 | {
25 | if (options.Profile == null || !string.IsNullOrWhiteSpace(section.Profile.ProfileDirectory) || section.Profile.DeleteSourceOnClean != null)
26 | options.Profile = CreateProfile(section.Profile);
27 |
28 | MapProfile(section.Profile, options.Profile);
29 | }
30 |
31 | if (section.Preferences != null)
32 | {
33 | foreach (var item in section.Preferences.ExtraPropertiesMap)
34 | SetOptionsPreference(item.Key, item.Value, options);
35 | }
36 |
37 | if (section.AndroidOptions != null)
38 | options.AndroidOptions = CreateAndMapAndroidOptions(section.AndroidOptions);
39 | }
40 |
41 | private static void SetOptionsPreference(string name, object value, FirefoxOptions options)
42 | {
43 | switch (value)
44 | {
45 | case bool castedValue:
46 | options.SetPreference(name, castedValue);
47 | break;
48 | case int castedValue:
49 | options.SetPreference(name, castedValue);
50 | break;
51 | case long castedValue:
52 | options.SetPreference(name, castedValue);
53 | break;
54 | case double castedValue:
55 | options.SetPreference(name, castedValue);
56 | break;
57 | case string castedValue:
58 | options.SetPreference(name, FillTemplateVariables(castedValue));
59 | break;
60 | case null:
61 | options.SetPreference(name, null);
62 | break;
63 | default:
64 | throw new ArgumentException($"Unsupported {nameof(FirefoxOptions)} preference value type: {value.GetType().FullName}. Supports: bool, int, long, double, string.", nameof(value));
65 | }
66 | }
67 |
68 | private static FirefoxProfile CreateProfile(DriverProfileJsonSection section)
69 | {
70 | string profileDirectory = string.IsNullOrWhiteSpace(section.ProfileDirectory)
71 | ? null
72 | : section.ProfileDirectory;
73 |
74 | return new FirefoxProfile(profileDirectory, section.DeleteSourceOnClean ?? false);
75 | }
76 |
77 | private static void SetProfilePreference(string name, object value, FirefoxProfile profile)
78 | {
79 | switch (value)
80 | {
81 | case bool castedValue:
82 | profile.SetPreference(name, castedValue);
83 | break;
84 | case int castedValue:
85 | profile.SetPreference(name, castedValue);
86 | break;
87 | case string castedValue:
88 | profile.SetPreference(name, FillTemplateVariables(castedValue));
89 | break;
90 | case null:
91 | throw new ArgumentNullException(nameof(value), $"Unsupported {nameof(FirefoxProfile)} preference value: null. Supports: string, int, bool.");
92 | default:
93 | throw new ArgumentException($"Unsupported {nameof(FirefoxProfile)} preference value type: {value.GetType().FullName}. Supports: bool, int, string.", nameof(value));
94 | }
95 | }
96 |
97 | private void MapProfile(DriverProfileJsonSection section, FirefoxProfile profile)
98 | {
99 | ObjectMapper.Map(section.ExtraPropertiesMap, profile);
100 |
101 | if (section.Extensions != null)
102 | {
103 | foreach (var item in section.Extensions)
104 | profile.AddExtension(item);
105 | }
106 |
107 | if (section.Preferences != null)
108 | {
109 | foreach (var item in section.Preferences.ExtraPropertiesMap)
110 | SetProfilePreference(item.Key, item.Value, profile);
111 | }
112 | }
113 |
114 | private FirefoxAndroidOptions CreateAndMapAndroidOptions(AndroidOptionsJsonSection section)
115 | {
116 | if (string.IsNullOrEmpty(section.AndroidPackage))
117 | throw new ConfigurationException(
118 | "\"androidPackage\" configuration property of \"androidOptions\" section is not specified.");
119 |
120 | var androidOptions = new FirefoxAndroidOptions(FillTemplateVariables(section.AndroidPackage));
121 | ObjectMapper.Map(section.ExtraPropertiesMap, androidOptions);
122 |
123 | if (section.AndroidIntentArguments != null)
124 | androidOptions.AddIntentArguments(section.AndroidIntentArguments);
125 |
126 | return androidOptions;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/CustomSettingsTests.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json.Tests;
2 |
3 | [TestFixture]
4 | public class CustomSettingsTests : TestFixture
5 | {
6 | [Test]
7 | public void Regular()
8 | {
9 | AtataContext.Configure()
10 | .ApplyJsonConfig(@"Configs/CustomSettings.json");
11 |
12 | CustomJsonConfig.Current.BaseUrl.Should().Be("https://demo.atata.io/");
13 | CustomJsonConfig.Current.IntProperty.Should().Be(5);
14 | CustomJsonConfig.Current.StringProperty.Should().Be("str");
15 | CustomJsonConfig.Current.BoolProperty.Should().Be(true);
16 | CustomJsonConfig.Current.BoolProperty2.Should().Be(false);
17 | CustomJsonConfig.Current.StringArrayValues.Should().Equal("str1", "str2", "str3");
18 | CustomJsonConfig.Current.StringListValues.Should().Equal("str1", "str2", "str3");
19 |
20 | CustomJsonConfig.Current.Section.StringProperty.Should().Be("section_str");
21 | CustomJsonConfig.Current.Section.BoolProperty.Should().Be(true);
22 |
23 | CustomJsonConfig.Current.Items.Should().BeEquivalentTo([
24 | new CustomJsonConfig.CustomItemSection
25 | {
26 | Name = "item1",
27 | Value = 5
28 | },
29 | new CustomJsonConfig.CustomItemSection
30 | {
31 | Name = "item2",
32 | Value = 7
33 | }
34 | ]);
35 | }
36 |
37 | [Test]
38 | public void Merged()
39 | {
40 | AtataContext.Configure()
41 | .ApplyJsonConfig(@"Configs/CustomSettings.json")
42 | .ApplyJsonConfig(@"Configs/CustomSettingsOverride.json");
43 |
44 | CustomJsonConfig.Current.BaseUrl.Should().Be("https://demo.atata.io/override");
45 | CustomJsonConfig.Current.IntProperty.Should().Be(5);
46 | CustomJsonConfig.Current.StringProperty.Should().Be("str2");
47 | CustomJsonConfig.Current.BoolProperty.Should().Be(true);
48 | CustomJsonConfig.Current.BoolProperty2.Should().Be(false);
49 | CustomJsonConfig.Current.StringArrayValues.Should().Equal("str4");
50 | CustomJsonConfig.Current.StringListValues.Should().Equal("str1", "str2", "str3", "str4");
51 |
52 | CustomJsonConfig.Current.Section.StringProperty.Should().Be("section_str");
53 | CustomJsonConfig.Current.Section.BoolProperty.Should().Be(false);
54 |
55 | CustomJsonConfig.Current.Items.Should().BeEquivalentTo([
56 | new CustomJsonConfig.CustomItemSection
57 | {
58 | Name = "item1",
59 | Value = 5
60 | },
61 | new CustomJsonConfig.CustomItemSection
62 | {
63 | Name = "item2",
64 | Value = 7
65 | },
66 | new CustomJsonConfig.CustomItemSection
67 | {
68 | Name = "item3",
69 | Value = 9
70 | }
71 | ]);
72 |
73 | CustomJsonConfig.Current.Drivers.Should().HaveCount(2);
74 | CustomJsonConfig.Current.Driver.Options.Arguments.Should().Equal("headless=new");
75 | }
76 |
77 | [Test]
78 | public void GlobalThenCurrent()
79 | {
80 | AtataContext.GlobalConfiguration
81 | .ApplyJsonConfig(@"Configs/CustomSettings.json");
82 |
83 | AtataContext primaryAtataContext = AtataContext.Configure()
84 | .ApplyJsonConfig(@"Configs/CustomSettingsOverride.json")
85 | .UseAllNUnitFeatures()
86 | .Build();
87 |
88 | CustomJsonConfig.Global.Driver.Options.Arguments.Should().Equal("start-maximized");
89 | CustomJsonConfig.Current.Driver.Options.Arguments.Should().Equal("headless=new");
90 |
91 | CustomJsonConfig.Global.BaseUrl.Should().Be("https://demo.atata.io/");
92 | CustomJsonConfig.Current.BaseUrl.Should().Be("https://demo.atata.io/override");
93 |
94 | CustomJsonConfig.Global.StringProperty.Should().Be("str");
95 | CustomJsonConfig.Current.StringProperty.Should().Be("str2");
96 |
97 | CustomJsonConfig.Global.StringListValues.Should().Equal("str1", "str2", "str3");
98 | CustomJsonConfig.Current.StringListValues.Should().Equal("str1", "str2", "str3", "str4");
99 |
100 | AtataContext secondaryAtataContext = null;
101 |
102 | Task.Run(() =>
103 | {
104 | AtataContext.Current.Should().Be(primaryAtataContext);
105 |
106 | secondaryAtataContext = AtataContext.Configure()
107 | .ApplyJsonConfig(@"Configs/CustomSettingsOverride2.json")
108 | .Build();
109 | }).GetAwaiter().GetResult();
110 |
111 | try
112 | {
113 | CustomJsonConfig.Global.BaseUrl.Should().Be("https://demo.atata.io/");
114 |
115 | CustomJsonConfig.Current.BaseUrl.Should().Be("https://demo.atata.io/override2");
116 | CustomJsonConfig.Current.StringProperty.Should().Be("str3");
117 |
118 | CustomJsonConfig.Current.StringListValues.Should().Equal("str1", "str2", "str3", "str4", "str5");
119 |
120 | // TODO: The above line should actually be:
121 | ////CustomJsonConfig.Current.StringListValues.Should().Equal(new[] { "str1", "str2", "str3", "str5" });
122 |
123 | AtataContext.Current.Dispose();
124 |
125 | CustomJsonConfig.Current.Should().BeNull();
126 |
127 | CustomJsonConfig.Global.BaseUrl.Should().Be("https://demo.atata.io/");
128 | CustomJsonConfig.Global.StringProperty.Should().Be("str");
129 | CustomJsonConfig.Global.StringListValues.Should().Equal("str1", "str2", "str3");
130 | }
131 | finally
132 | {
133 | primaryAtataContext?.Dispose();
134 | secondaryAtataContext?.Dispose();
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/test/Atata.Configuration.Json.Tests/Atata.Configuration.Json.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | 12.0
6 | disable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | PreserveNewest
25 |
26 |
27 | PreserveNewest
28 |
29 |
30 | PreserveNewest
31 |
32 |
33 | PreserveNewest
34 |
35 |
36 | PreserveNewest
37 |
38 |
39 | PreserveNewest
40 |
41 |
42 | PreserveNewest
43 |
44 |
45 | PreserveNewest
46 |
47 |
48 | PreserveNewest
49 |
50 |
51 | PreserveNewest
52 |
53 |
54 | PreserveNewest
55 |
56 |
57 | PreserveNewest
58 |
59 |
60 | PreserveNewest
61 |
62 |
63 | PreserveNewest
64 |
65 |
66 | PreserveNewest
67 |
68 |
69 | PreserveNewest
70 |
71 |
72 | PreserveNewest
73 |
74 |
75 | PreserveNewest
76 |
77 |
78 | PreserveNewest
79 |
80 |
81 | PreserveNewest
82 |
83 |
84 | PreserveNewest
85 |
86 |
87 | PreserveNewest
88 |
89 |
90 | PreserveNewest
91 |
92 |
93 | PreserveNewest
94 |
95 |
96 | PreserveNewest
97 |
98 |
99 | PreserveNewest
100 |
101 |
102 | PreserveNewest
103 |
104 |
105 | PreserveNewest
106 |
107 |
108 | PreserveNewest
109 |
110 |
111 | PreserveNewest
112 |
113 |
114 | PreserveNewest
115 |
116 |
117 | PreserveNewest
118 |
119 |
120 | PreserveNewest
121 |
122 |
123 | PreserveNewest
124 |
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/JsonConfigFile.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | ///
4 | /// Provides static methods for loading of JSON configuration from file.
5 | ///
6 | public static class JsonConfigFile
7 | {
8 | ///
9 | /// The default file name is "Atata".
10 | ///
11 | public const string DefaultFileName = "Atata";
12 |
13 | ///
14 | /// The default file extension is ".json".
15 | ///
16 | public const string DefaultFileExtension = ".json";
17 |
18 | ///
19 | /// Reads the JSON config file and deserializes it to an object of type.
20 | ///
21 | /// The file path.
22 | /// The environment alias.
23 | /// The deserialized object of type.
24 | public static JsonConfig Read(string filePath = null, string environmentAlias = null) =>
25 | Read(filePath, environmentAlias);
26 |
27 | ///
28 | /// Reads the JSON config file and deserializes it to an object of type.
29 | ///
30 | /// The type of the configuration class.
31 | /// The file path.
32 | /// The environment alias.
33 | /// The deserialized object of type.
34 | public static TConfig Read(string filePath = null, string environmentAlias = null)
35 | {
36 | string jsonContent = ReadText(filePath, environmentAlias);
37 |
38 | return JsonConvert.DeserializeObject(jsonContent);
39 | }
40 |
41 | ///
42 | /// Reads the text from the JSON config file.
43 | ///
44 | /// The file path.
45 | /// The environment alias.
46 | /// A string containing content of JSON file.
47 | public static string ReadText(string filePath = null, string environmentAlias = null)
48 | {
49 | string fullFilePath = GetFullPath(filePath, environmentAlias);
50 |
51 | string jsonText = File.ReadAllText(fullFilePath);
52 | return ProcessJsonText(jsonText);
53 | }
54 |
55 | private static string ProcessJsonText(string jsonText)
56 | {
57 | Regex regex = new Regex(@"{env:\D[^}]*}");
58 |
59 | return regex.Replace(jsonText, match =>
60 | {
61 | string variableName = match.Value.Substring(5, match.Value.Length - 6);
62 |
63 | string variableValue = Environment.GetEnvironmentVariable(variableName);
64 |
65 | return variableValue != null
66 | ? EscapeToJsonString(variableValue)
67 | : throw new ConfigurationException($@"""{variableName}"" environment variable is not found.");
68 | });
69 | }
70 |
71 | private static string EscapeToJsonString(string value)
72 | {
73 | string escaped = JsonConvert.ToString(value);
74 | return escaped.Substring(1, escaped.Length - 2);
75 | }
76 |
77 | ///
78 | /// Reads the default "Atata.json" config file, deserializes it and returns the driver aliases.
79 | ///
80 | /// An array of the driver aliases.
81 | public static string[] ReadDriverAliasesFromDefaultConfig() =>
82 | ReadDriverAliases();
83 |
84 | ///
85 | /// Reads the JSON config file, deserializes it and returns the driver aliases.
86 | ///
87 | /// The file path.
88 | /// The environment alias.
89 | /// An array of the driver aliases.
90 | public static string[] ReadDriverAliases(string filePath = null, string environmentAlias = null) =>
91 | Read(filePath, environmentAlias)
92 | .Drivers?.Select(x => x.Alias)
93 | .ToArray()
94 | ?? [];
95 |
96 | ///
97 | /// Returns the full/absolute path for the file using optionally and arguments.
98 | ///
99 | /// The file path.
100 | /// The environment alias.
101 | /// The full file path.
102 | public static string GetFullPath(string filePath = null, string environmentAlias = null)
103 | {
104 | string path = GetRelativePath(filePath, environmentAlias);
105 |
106 | return Path.IsPathRooted(path)
107 | ? path
108 | : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
109 | }
110 |
111 | ///
112 | /// Returns the relative path for the file using optionally and arguments.
113 | ///
114 | /// The file path.
115 | /// The environment alias.
116 | /// The relative file path.
117 | public static string GetRelativePath(string filePath = null, string environmentAlias = null)
118 | {
119 | string environmentAliasInsertion = string.IsNullOrWhiteSpace(environmentAlias)
120 | ? null
121 | : $".{environmentAlias}";
122 |
123 | if (string.IsNullOrWhiteSpace(filePath))
124 | {
125 | return $"{DefaultFileName}{environmentAliasInsertion}{DefaultFileExtension}";
126 | }
127 | else
128 | {
129 | if (filePath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
130 | {
131 | return $"{filePath}{DefaultFileName}{environmentAliasInsertion}{DefaultFileExtension}";
132 | }
133 | else if (Path.HasExtension(filePath))
134 | {
135 | return environmentAliasInsertion == null
136 | ? filePath
137 | : $"{Path.GetFileNameWithoutExtension(filePath)}{environmentAliasInsertion}{Path.GetExtension(filePath)}";
138 | }
139 | else
140 | {
141 | return $"{filePath}{environmentAliasInsertion}{DefaultFileExtension}";
142 | }
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/JsonConfig`1.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | ///
4 | /// Represents JSON configuration.
5 | ///
6 | /// The type of the configuration class.
7 | public abstract class JsonConfig : JsonSection
8 | where TConfig : JsonConfig
9 | {
10 | private static readonly AsyncLocal s_currentAsyncLocalConfig = new();
11 |
12 | [ThreadStatic]
13 | private static TConfig s_currentThreadStaticConfig;
14 |
15 | private static TConfig s_currentStaticConfig;
16 |
17 | ///
18 | /// Gets or sets the global instance.
19 | ///
20 | public static TConfig Global { get; set; }
21 |
22 | ///
23 | /// Gets or sets the current instance.
24 | /// Relies on value of .
25 | ///
26 | public static TConfig Current
27 | {
28 | get => AtataContext.GlobalProperties.ModeOfCurrent == AtataContextModeOfCurrent.AsyncLocal
29 | ? s_currentAsyncLocalConfig.Value
30 | : AtataContext.GlobalProperties.ModeOfCurrent == AtataContextModeOfCurrent.ThreadStatic
31 | ? s_currentThreadStaticConfig
32 | : s_currentStaticConfig;
33 | set
34 | {
35 | if (AtataContext.GlobalProperties.ModeOfCurrent == AtataContextModeOfCurrent.AsyncLocal)
36 | s_currentAsyncLocalConfig.Value = value;
37 | else if (AtataContext.GlobalProperties.ModeOfCurrent == AtataContextModeOfCurrent.ThreadStatic)
38 | s_currentThreadStaticConfig = value;
39 | else
40 | s_currentStaticConfig = value;
41 | }
42 | }
43 |
44 | public List Drivers { get; set; }
45 |
46 | [JsonConverter(typeof(JsonConverterWithoutPopulation))]
47 | public DriverJsonSection Driver
48 | {
49 | get => Drivers?.LastOrDefault();
50 | set
51 | {
52 | if (value != null)
53 | {
54 | Drivers ??= [];
55 | Drivers.Add(value);
56 | }
57 | }
58 | }
59 |
60 | ///
61 | /// Gets or sets the driver initialization stage.
62 | ///
63 | public AtataContextDriverInitializationStage? DriverInitializationStage { get; set; }
64 |
65 | public List LogConsumers { get; set; }
66 |
67 | public string BaseUrl { get; set; }
68 |
69 | public Visibility? DefaultControlVisibility { get; set; }
70 |
71 | public string Culture { get; set; }
72 |
73 | ///
74 | /// Gets or sets the Artifacts directory path template.
75 | ///
76 | public string ArtifactsPathTemplate { get; set; }
77 |
78 | ///
79 | /// Gets or sets the variables.
80 | ///
81 | public Dictionary Variables { get; set; }
82 |
83 | ///
84 | /// Gets or sets the base retry timeout in seconds.
85 | ///
86 | public double? BaseRetryTimeout { get; set; }
87 |
88 | ///
89 | /// Gets or sets the base retry interval in seconds.
90 | ///
91 | public double? BaseRetryInterval { get; set; }
92 |
93 | ///
94 | /// Gets or sets the element find timeout in seconds.
95 | ///
96 | public double? ElementFindTimeout { get; set; }
97 |
98 | ///
99 | /// Gets or sets the element find retry interval in seconds.
100 | ///
101 | public double? ElementFindRetryInterval { get; set; }
102 |
103 | ///
104 | /// Gets or sets the waiting timeout in seconds.
105 | ///
106 | public double? WaitingTimeout { get; set; }
107 |
108 | ///
109 | /// Gets or sets the waiting retry interval in seconds.
110 | ///
111 | public double? WaitingRetryInterval { get; set; }
112 |
113 | ///
114 | /// Gets or sets the verification timeout in seconds.
115 | ///
116 | public double? VerificationTimeout { get; set; }
117 |
118 | ///
119 | /// Gets or sets the verification retry interval in seconds.
120 | ///
121 | public double? VerificationRetryInterval { get; set; }
122 |
123 | ///
124 | /// Gets or sets the type name of the assertion exception.
125 | ///
126 | public string AssertionExceptionType { get; set; }
127 |
128 | ///
129 | /// Gets or sets the type name of the aggregate assertion exception.
130 | /// The exception type should have public constructor with IEnumerable<AssertionResult> argument.
131 | ///
132 | public string AggregateAssertionExceptionType { get; set; }
133 |
134 | ///
135 | /// Gets or sets the type name of the aggregate assertion strategy.
136 | /// The type should implement .
137 | ///
138 | public string AggregateAssertionStrategyType { get; set; }
139 |
140 | ///
141 | /// Gets or sets the type name of the strategy for warning assertion reporting.
142 | /// The type should implement .
143 | ///
144 | public string WarningReportStrategyType { get; set; }
145 |
146 | ///
147 | /// Gets or sets the type name of the strategy for assertion failure reporting.
148 | /// The type should implement .
149 | ///
150 | public string AssertionFailureReportStrategyType { get; set; }
151 |
152 | ///
153 | /// Gets or sets the name of the DOM test identifier attribute.
154 | ///
155 | public string DomTestIdAttributeName { get; set; }
156 |
157 | ///
158 | /// Gets or sets the default case of the DOM test identifier attribute.
159 | ///
160 | public TermCase? DomTestIdAttributeDefaultCase { get; set; }
161 |
162 | ///
163 | /// Gets or sets a value indicating whether to use NUnit test name.
164 | ///
165 | public bool UseNUnitTestName { get; set; }
166 |
167 | ///
168 | /// Gets or sets a value indicating whether to use NUnit test suite (fixture) name.
169 | ///
170 | public bool UseNUnitTestSuiteName { get; set; }
171 |
172 | ///
173 | /// Gets or sets a value indicating whether to use NUnit test suite (fixture) type.
174 | ///
175 | public bool UseNUnitTestSuiteType { get; set; }
176 |
177 | ///
178 | /// Gets or sets a value indicating whether to use as the aggregate assertion strategy.
179 | ///
180 | public bool UseNUnitAggregateAssertionStrategy { get; set; }
181 |
182 | ///
183 | /// Gets or sets a value indicating whether to use as the strategy for warning assertion reporting.
184 | ///
185 | public bool UseNUnitWarningReportStrategy { get; set; }
186 |
187 | ///
188 | /// Gets or sets a value indicating whether to use as the strategy for assertion failure reporting.
189 | ///
190 | public bool UseNUnitAssertionFailureReportStrategy { get; set; }
191 |
192 | ///
193 | /// Gets or sets a value indicating whether to enable all Atata features for NUnit.
194 | ///
195 | public bool UseAllNUnitFeatures { get; set; }
196 |
197 | ///
198 | /// Gets or sets a value indicating whether to enable all Atata features for SpecFlow+NUnit.
199 | ///
200 | public bool UseSpecFlowNUnitFeatures { get; set; }
201 |
202 | ///
203 | /// Gets or sets the default assembly name pattern that is used to filter assemblies to find types in them.
204 | ///
205 | public string DefaultAssemblyNamePatternToFindTypes { get; set; }
206 |
207 | ///
208 | /// Gets or sets the assembly name pattern that is used to filter assemblies to find component types in them.
209 | ///
210 | public string AssemblyNamePatternToFindComponentTypes { get; set; }
211 |
212 | ///
213 | /// Gets or sets the assembly name pattern that is used to filter assemblies to find attribute types in them.
214 | ///
215 | public string AssemblyNamePatternToFindAttributeTypes { get; set; }
216 |
217 | ///
218 | /// Gets or sets the assembly name pattern that is used to filter assemblies to find event types in them.
219 | ///
220 | public string AssemblyNamePatternToFindEventTypes { get; set; }
221 |
222 | ///
223 | /// Gets or sets the assembly name pattern that is used to filter assemblies to find event handler types in them.
224 | ///
225 | public string AssemblyNamePatternToFindEventHandlerTypes { get; set; }
226 |
227 | ///
228 | /// Gets or sets the attributes.
229 | ///
230 | public AttributesJsonSection Attributes { get; set; }
231 |
232 | ///
233 | /// Gets or sets the event subscriptions.
234 | ///
235 | public List EventSubscriptions { get; set; }
236 |
237 | ///
238 | /// Gets or sets the screenshots configuration.
239 | ///
240 | public ScreenshotsJsonSection Screenshots { get; set; }
241 |
242 | ///
243 | /// Gets or sets the page snapshots configuration.
244 | ///
245 | public PageSnapshotsJsonSection PageSnapshots { get; set; }
246 |
247 | ///
248 | /// Gets or sets the browser logs configuration.
249 | ///
250 | public BrowserLogsJsonSection BrowserLogs { get; set; }
251 | }
252 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from `dotnet new gitignore`
5 |
6 | # dotenv files
7 | .env
8 |
9 | # User-specific files
10 | *.rsuser
11 | *.suo
12 | *.user
13 | *.userosscache
14 | *.sln.docstates
15 |
16 | # User-specific files (MonoDevelop/Xamarin Studio)
17 | *.userprefs
18 |
19 | # Mono auto generated files
20 | mono_crash.*
21 |
22 | # Build results
23 | [Dd]ebug/
24 | [Dd]ebugPublic/
25 | [Rr]elease/
26 | [Rr]eleases/
27 | x64/
28 | x86/
29 | [Ww][Ii][Nn]32/
30 | [Aa][Rr][Mm]/
31 | [Aa][Rr][Mm]64/
32 | bld/
33 | [Bb]in/
34 | [Oo]bj/
35 | [Ll]og/
36 | [Ll]ogs/
37 |
38 | # Visual Studio 2015/2017 cache/options directory
39 | .vs/
40 | # Uncomment if you have tasks that create the project's static files in wwwroot
41 | wwwroot/
42 |
43 | # Visual Studio 2017 auto generated files
44 | Generated\ Files/
45 |
46 | # MSTest test Results
47 | [Tt]est[Rr]esult*/
48 | [Bb]uild[Ll]og.*
49 |
50 | # NUnit
51 | *.VisualState.xml
52 | TestResult.xml
53 | nunit-*.xml
54 |
55 | # Build Results of an ATL Project
56 | [Dd]ebugPS/
57 | [Rr]eleasePS/
58 | dlldata.c
59 |
60 | # Benchmark Results
61 | BenchmarkDotNet.Artifacts/
62 |
63 | # .NET
64 | project.lock.json
65 | project.fragment.lock.json
66 | artifacts/
67 |
68 | # Tye
69 | .tye/
70 |
71 | # ASP.NET Scaffolding
72 | ScaffoldingReadMe.txt
73 |
74 | # StyleCop
75 | StyleCopReport.xml
76 |
77 | # Files built by Visual Studio
78 | *_i.c
79 | *_p.c
80 | *_h.h
81 | *.ilk
82 | *.meta
83 | *.obj
84 | *.iobj
85 | *.pch
86 | *.pdb
87 | *.ipdb
88 | *.pgc
89 | *.pgd
90 | *.rsp
91 | *.sbr
92 | *.tlb
93 | *.tli
94 | *.tlh
95 | *.tmp
96 | *.tmp_proj
97 | *_wpftmp.csproj
98 | *.log
99 | *.tlog
100 | *.vspscc
101 | *.vssscc
102 | .builds
103 | *.pidb
104 | *.svclog
105 | *.scc
106 |
107 | # Chutzpah Test files
108 | _Chutzpah*
109 |
110 | # Visual C++ cache files
111 | ipch/
112 | *.aps
113 | *.ncb
114 | *.opendb
115 | *.opensdf
116 | *.sdf
117 | *.cachefile
118 | *.VC.db
119 | *.VC.VC.opendb
120 |
121 | # Visual Studio profiler
122 | *.psess
123 | *.vsp
124 | *.vspx
125 | *.sap
126 |
127 | # Visual Studio Trace Files
128 | *.e2e
129 |
130 | # TFS 2012 Local Workspace
131 | $tf/
132 |
133 | # Guidance Automation Toolkit
134 | *.gpState
135 |
136 | # ReSharper is a .NET coding add-in
137 | _ReSharper*/
138 | *.[Rr]e[Ss]harper
139 | *.DotSettings.user
140 |
141 | # TeamCity is a build add-in
142 | _TeamCity*
143 |
144 | # DotCover is a Code Coverage Tool
145 | *.dotCover
146 |
147 | # AxoCover is a Code Coverage Tool
148 | .axoCover/*
149 | !.axoCover/settings.json
150 |
151 | # Coverlet is a free, cross platform Code Coverage Tool
152 | coverage*.json
153 | coverage*.xml
154 | coverage*.info
155 |
156 | # Visual Studio code coverage results
157 | *.coverage
158 | *.coveragexml
159 |
160 | # NCrunch
161 | _NCrunch_*
162 | .*crunch*.local.xml
163 | nCrunchTemp_*
164 |
165 | # MightyMoose
166 | *.mm.*
167 | AutoTest.Net/
168 |
169 | # Web workbench (sass)
170 | .sass-cache/
171 |
172 | # Installshield output folder
173 | [Ee]xpress/
174 |
175 | # DocProject is a documentation generator add-in
176 | DocProject/buildhelp/
177 | DocProject/Help/*.HxT
178 | DocProject/Help/*.HxC
179 | DocProject/Help/*.hhc
180 | DocProject/Help/*.hhk
181 | DocProject/Help/*.hhp
182 | DocProject/Help/Html2
183 | DocProject/Help/html
184 |
185 | # Click-Once directory
186 | publish/
187 |
188 | # Publish Web Output
189 | *.[Pp]ublish.xml
190 | *.azurePubxml
191 | # Note: Comment the next line if you want to checkin your web deploy settings,
192 | # but database connection strings (with potential passwords) will be unencrypted
193 | *.pubxml
194 | *.publishproj
195 |
196 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
197 | # checkin your Azure Web App publish settings, but sensitive information contained
198 | # in these scripts will be unencrypted
199 | PublishScripts/
200 |
201 | # NuGet Packages
202 | *.nupkg
203 | # NuGet Symbol Packages
204 | *.snupkg
205 | # The packages folder can be ignored because of Package Restore
206 | **/[Pp]ackages/*
207 | # except build/, which is used as an MSBuild target.
208 | !**/[Pp]ackages/build/
209 | # Uncomment if necessary however generally it will be regenerated when needed
210 | #!**/[Pp]ackages/repositories.config
211 | # NuGet v3's project.json files produces more ignorable files
212 | *.nuget.props
213 | *.nuget.targets
214 |
215 | # Microsoft Azure Build Output
216 | csx/
217 | *.build.csdef
218 |
219 | # Microsoft Azure Emulator
220 | ecf/
221 | rcf/
222 |
223 | # Windows Store app package directories and files
224 | AppPackages/
225 | BundleArtifacts/
226 | Package.StoreAssociation.xml
227 | _pkginfo.txt
228 | *.appx
229 | *.appxbundle
230 | *.appxupload
231 |
232 | # Visual Studio cache files
233 | # files ending in .cache can be ignored
234 | *.[Cc]ache
235 | # but keep track of directories ending in .cache
236 | !?*.[Cc]ache/
237 |
238 | # Others
239 | ClientBin/
240 | ~$*
241 | *~
242 | *.dbmdl
243 | *.dbproj.schemaview
244 | *.jfm
245 | *.pfx
246 | *.publishsettings
247 | orleans.codegen.cs
248 |
249 | # Including strong name files can present a security risk
250 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
251 | #*.snk
252 |
253 | # Since there are multiple workflows, uncomment next line to ignore bower_components
254 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
255 | #bower_components/
256 |
257 | # RIA/Silverlight projects
258 | Generated_Code/
259 |
260 | # Backup & report files from converting an old project file
261 | # to a newer Visual Studio version. Backup files are not needed,
262 | # because we have git ;-)
263 | _UpgradeReport_Files/
264 | Backup*/
265 | UpgradeLog*.XML
266 | UpgradeLog*.htm
267 | ServiceFabricBackup/
268 | *.rptproj.bak
269 |
270 | # SQL Server files
271 | *.mdf
272 | *.ldf
273 | *.ndf
274 |
275 | # Business Intelligence projects
276 | *.rdl.data
277 | *.bim.layout
278 | *.bim_*.settings
279 | *.rptproj.rsuser
280 | *- [Bb]ackup.rdl
281 | *- [Bb]ackup ([0-9]).rdl
282 | *- [Bb]ackup ([0-9][0-9]).rdl
283 |
284 | # Microsoft Fakes
285 | FakesAssemblies/
286 |
287 | # GhostDoc plugin setting file
288 | *.GhostDoc.xml
289 |
290 | # Node.js Tools for Visual Studio
291 | .ntvs_analysis.dat
292 | node_modules/
293 |
294 | # Visual Studio 6 build log
295 | *.plg
296 |
297 | # Visual Studio 6 workspace options file
298 | *.opt
299 |
300 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
301 | *.vbw
302 |
303 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
304 | *.vbp
305 |
306 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
307 | *.dsw
308 | *.dsp
309 |
310 | # Visual Studio 6 technical files
311 | *.ncb
312 | *.aps
313 |
314 | # Visual Studio LightSwitch build output
315 | **/*.HTMLClient/GeneratedArtifacts
316 | **/*.DesktopClient/GeneratedArtifacts
317 | **/*.DesktopClient/ModelManifest.xml
318 | **/*.Server/GeneratedArtifacts
319 | **/*.Server/ModelManifest.xml
320 | _Pvt_Extensions
321 |
322 | # Paket dependency manager
323 | .paket/paket.exe
324 | paket-files/
325 |
326 | # FAKE - F# Make
327 | .fake/
328 |
329 | # CodeRush personal settings
330 | .cr/personal
331 |
332 | # Python Tools for Visual Studio (PTVS)
333 | __pycache__/
334 | *.pyc
335 |
336 | # Cake - Uncomment if you are using it
337 | # tools/**
338 | # !tools/packages.config
339 |
340 | # Tabs Studio
341 | *.tss
342 |
343 | # Telerik's JustMock configuration file
344 | *.jmconfig
345 |
346 | # BizTalk build output
347 | *.btp.cs
348 | *.btm.cs
349 | *.odx.cs
350 | *.xsd.cs
351 |
352 | # OpenCover UI analysis results
353 | OpenCover/
354 |
355 | # Azure Stream Analytics local run output
356 | ASALocalRun/
357 |
358 | # MSBuild Binary and Structured Log
359 | *.binlog
360 |
361 | # NVidia Nsight GPU debugger configuration file
362 | *.nvuser
363 |
364 | # MFractors (Xamarin productivity tool) working folder
365 | .mfractor/
366 |
367 | # Local History for Visual Studio
368 | .localhistory/
369 |
370 | # Visual Studio History (VSHistory) files
371 | .vshistory/
372 |
373 | # BeatPulse healthcheck temp database
374 | healthchecksdb
375 |
376 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
377 | MigrationBackup/
378 |
379 | # Ionide (cross platform F# VS Code tools) working folder
380 | .ionide/
381 |
382 | # Fody - auto-generated XML schema
383 | FodyWeavers.xsd
384 |
385 | # VS Code files for those working on multiple tools
386 | .vscode/*
387 | !.vscode/settings.json
388 | !.vscode/tasks.json
389 | !.vscode/launch.json
390 | !.vscode/extensions.json
391 | *.code-workspace
392 |
393 | # Local History for Visual Studio Code
394 | .history/
395 |
396 | # Windows Installer files from build outputs
397 | *.cab
398 | *.msi
399 | *.msix
400 | *.msm
401 | *.msp
402 |
403 | # JetBrains Rider
404 | *.sln.iml
405 | .idea/
406 |
407 | ##
408 | ## Visual studio for Mac
409 | ##
410 |
411 |
412 | # globs
413 | Makefile.in
414 | *.userprefs
415 | *.usertasks
416 | config.make
417 | config.status
418 | aclocal.m4
419 | install-sh
420 | autom4te.cache/
421 | *.tar.gz
422 | tarballs/
423 | test-results/
424 |
425 | # Mac bundle stuff
426 | *.dmg
427 | *.app
428 |
429 | # content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore
430 | # General
431 | .DS_Store
432 | .AppleDouble
433 | .LSOverride
434 |
435 | # Icon must end with two \r
436 | Icon
437 |
438 |
439 | # Thumbnails
440 | ._*
441 |
442 | # Files that might appear in the root of a volume
443 | .DocumentRevisions-V100
444 | .fseventsd
445 | .Spotlight-V100
446 | .TemporaryItems
447 | .Trashes
448 | .VolumeIcon.icns
449 | .com.apple.timemachine.donotpresent
450 |
451 | # Directories potentially created on remote AFP share
452 | .AppleDB
453 | .AppleDesktop
454 | Network Trash Folder
455 | Temporary Items
456 | .apdisk
457 |
458 | # content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore
459 | # Windows thumbnail cache files
460 | Thumbs.db
461 | ehthumbs.db
462 | ehthumbs_vista.db
463 |
464 | # Dump file
465 | *.stackdump
466 |
467 | # Folder config file
468 | [Dd]esktop.ini
469 |
470 | # Recycle Bin used on file shares
471 | $RECYCLE.BIN/
472 |
473 | # Windows Installer files
474 | *.cab
475 | *.msi
476 | *.msix
477 | *.msm
478 | *.msp
479 |
480 | # Windows shortcuts
481 | *.lnk
482 |
483 | # Vim temporary swap files
484 | *.swp
485 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | #pragma warning disable S103 // Lines should not be too long
4 |
5 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverJsonSection.PortsToIgnore")]
6 | [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverOptionsJsonSection.LoggingPreferences")]
7 | [assembly: SuppressMessage("Major Code Smell", "S4004:Collection properties should be readonly", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverOptionsJsonSection.LoggingPreferences")]
8 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverOptionsJsonSection.Arguments")]
9 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverOptionsJsonSection.ExcludedArguments")]
10 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverOptionsJsonSection.Extensions")]
11 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverOptionsJsonSection.EncodedExtensions")]
12 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverOptionsJsonSection.WindowTypes")]
13 | [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.Drivers")]
14 | [assembly: SuppressMessage("Major Code Smell", "S4004:Collection properties should be readonly", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.Drivers")]
15 | [assembly: SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.Global")]
16 | [assembly: SuppressMessage("Major Code Smell", "S2743:Static fields should not be used in generic types", Justification = "", Scope = "member", Target = "~F:Atata.Configuration.Json.JsonConfig`1.s_currentThreadStaticConfig")]
17 | [assembly: SuppressMessage("Major Code Smell", "S2743:Static fields should not be used in generic types", Justification = "", Scope = "member", Target = "~F:Atata.Configuration.Json.JsonConfig`1.s_currentStaticConfig")]
18 | [assembly: SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.Current")]
19 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverPerformanceLoggingPreferencesJsonSection.TracingCategories")]
20 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.DriverProfileJsonSection.Extensions")]
21 | [assembly: SuppressMessage("Major Code Smell", "S138:Functions should not have too many lines of code", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.JsonConfigMapper.Map``1(``0,Atata.AtataContextBuilder)~Atata.AtataContextBuilder")]
22 | [assembly: SuppressMessage("Critical Code Smell", "S1541:Methods and properties should not be too complex", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.JsonConfigMapper.Map``1(``0,Atata.AtataContextBuilder)~Atata.AtataContextBuilder")]
23 | [assembly: SuppressMessage("Critical Code Smell", "S1541:Methods and properties should not be too complex", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.JsonConfigMapper.MapAttributes(Atata.Configuration.Json.AttributesJsonSection,Atata.AtataContextBuilder)")]
24 | [assembly: SuppressMessage("Critical Code Smell", "S134:Control flow statements \"if\", \"switch\", \"for\", \"foreach\", \"while\", \"do\" and \"try\" should not be nested too deeply", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.JsonConfigMapper.MapAttributes(Atata.Configuration.Json.AttributesJsonSection,Atata.AtataContextBuilder)")]
25 | [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.AttributesJsonSection.Global")]
26 | [assembly: SuppressMessage("Major Code Smell", "S4004:Collection properties should be readonly", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.AttributesJsonSection.Global")]
27 | [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.AttributesJsonSection.Assembly")]
28 | [assembly: SuppressMessage("Major Code Smell", "S4004:Collection properties should be readonly", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.AttributesJsonSection.Assembly")]
29 | [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.AttributesJsonSection.Component")]
30 | [assembly: SuppressMessage("Major Code Smell", "S4004:Collection properties should be readonly", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.AttributesJsonSection.Component")]
31 | [assembly: SuppressMessage("Critical Code Smell", "S2302:\"nameof\" should be used", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.AttributeMapper.Map(Atata.Configuration.Json.AttributeJsonSection)~System.Attribute")]
32 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.ProxyJsonSection.BypassAddresses")]
33 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.ComponentAttributesJsonSection.Attributes")]
34 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.ComponentAttributesJsonSection.Properties")]
35 | [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.LogConsumers")]
36 | [assembly: SuppressMessage("Major Code Smell", "S4004:Collection properties should be readonly", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.LogConsumers")]
37 | [assembly: SuppressMessage("Critical Code Smell", "S1541:Methods and properties should not be too complex", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.DriverJsonMapper`3.Map(Atata.Configuration.Json.DriverJsonSection,`0)")]
38 | [assembly: SuppressMessage("Critical Code Smell", "S1541:Methods and properties should not be too complex", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.FirefoxDriverJsonMapper.MapOptions(Atata.Configuration.Json.DriverOptionsJsonSection,OpenQA.Selenium.Firefox.FirefoxOptions)")]
39 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.AssemblyAttributesJsonSection.Attributes")]
40 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.PropertyAttributesJsonSection.Attributes")]
41 | [assembly: SuppressMessage("Critical Code Smell", "S1541:Methods and properties should not be too complex", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.ChromeDriverJsonMapper.MapOptions(Atata.Configuration.Json.DriverOptionsJsonSection,OpenQA.Selenium.Chrome.ChromeOptions)")]
42 | [assembly: SuppressMessage("Major Code Smell", "S2743:Static fields should not be used in generic types", Justification = "", Scope = "member", Target = "~F:Atata.Configuration.Json.JsonConfigManager`1.s_serializerSettings")]
43 | [assembly: SuppressMessage("Critical Code Smell", "S2302:\"nameof\" should be used", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.EventSubscriptionMapper.Map(Atata.Configuration.Json.EventSubscriptionJsonSection)~Atata.EventSubscriptionItem")]
44 | [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.EventSubscriptions")]
45 | [assembly: SuppressMessage("Major Code Smell", "S4004:Collection properties should be readonly", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.EventSubscriptions")]
46 | [assembly: SuppressMessage("Critical Code Smell", "S1541:Methods and properties should not be too complex", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.ChromiumDriverJsonMapper`3.MapOptions(Atata.Configuration.Json.DriverOptionsJsonSection,`2)")]
47 | [assembly: SuppressMessage("Critical Code Smell", "S1541:Methods and properties should not be too complex", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.DriverJsonMapper`3.MapOptions(Atata.Configuration.Json.DriverOptionsJsonSection,`2)")]
48 | [assembly: SuppressMessage("Critical Code Smell", "S2302:\"nameof\" should be used", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.ChromiumDriverJsonMapper`3.CreateAndMapAndroidOptions(Atata.Configuration.Json.AndroidOptionsJsonSection)~OpenQA.Selenium.Chromium.ChromiumAndroidOptions")]
49 | [assembly: SuppressMessage("Critical Code Smell", "S2302:\"nameof\" should be used", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.FirefoxDriverJsonMapper.CreateAndMapAndroidOptions(Atata.Configuration.Json.AndroidOptionsJsonSection)~OpenQA.Selenium.Firefox.FirefoxAndroidOptions")]
50 | [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.AndroidOptionsJsonSection.AndroidIntentArguments")]
51 | [assembly: SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.Variables")]
52 | [assembly: SuppressMessage("Major Code Smell", "S4004:Collection properties should be readonly", Justification = "", Scope = "member", Target = "~P:Atata.Configuration.Json.JsonConfig`1.Variables")]
53 | [assembly: SuppressMessage("Critical Code Smell", "S1541:Methods and properties should not be too complex", Justification = "", Scope = "member", Target = "~M:Atata.Configuration.Json.JsonSection.ConvertJToken(Newtonsoft.Json.Linq.JToken)~System.Object")]
54 |
55 | #pragma warning restore S103 // Lines should not be too long
56 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/src/Atata.Configuration.Json/Mapping/JsonConfigMapper.cs:
--------------------------------------------------------------------------------
1 | namespace Atata.Configuration.Json;
2 |
3 | public static class JsonConfigMapper
4 | {
5 | public static AtataContextBuilder Map(TConfig config, AtataContextBuilder builder)
6 | where TConfig : JsonConfig
7 | {
8 | if (config.DriverInitializationStage is not null)
9 | builder.UseDriverInitializationStage(config.DriverInitializationStage.Value);
10 |
11 | if (config.BaseUrl is not null)
12 | builder.UseBaseUrl(config.BaseUrl);
13 |
14 | if (config.DefaultControlVisibility is not null)
15 | builder.UseDefaultControlVisibility(config.DefaultControlVisibility.Value);
16 |
17 | if (config.Culture is not null)
18 | builder.UseCulture(config.Culture);
19 |
20 | if (config.ArtifactsPathTemplate is not null)
21 | builder.UseArtifactsPathTemplate(config.ArtifactsPathTemplate);
22 |
23 | if (config.Variables is not null)
24 | builder.AddVariables(config.Variables);
25 |
26 | if (config.BaseRetryTimeout is not null)
27 | builder.UseBaseRetryTimeout(TimeSpan.FromSeconds(config.BaseRetryTimeout.Value));
28 |
29 | if (config.BaseRetryInterval is not null)
30 | builder.UseBaseRetryInterval(TimeSpan.FromSeconds(config.BaseRetryInterval.Value));
31 |
32 | if (config.ElementFindTimeout is not null)
33 | builder.UseElementFindTimeout(TimeSpan.FromSeconds(config.ElementFindTimeout.Value));
34 |
35 | if (config.ElementFindRetryInterval is not null)
36 | builder.UseElementFindRetryInterval(TimeSpan.FromSeconds(config.ElementFindRetryInterval.Value));
37 |
38 | if (config.WaitingTimeout is not null)
39 | builder.UseWaitingTimeout(TimeSpan.FromSeconds(config.WaitingTimeout.Value));
40 |
41 | if (config.WaitingRetryInterval is not null)
42 | builder.UseWaitingRetryInterval(TimeSpan.FromSeconds(config.WaitingRetryInterval.Value));
43 |
44 | if (config.VerificationTimeout is not null)
45 | builder.UseVerificationTimeout(TimeSpan.FromSeconds(config.VerificationTimeout.Value));
46 |
47 | if (config.VerificationRetryInterval is not null)
48 | builder.UseVerificationRetryInterval(TimeSpan.FromSeconds(config.VerificationRetryInterval.Value));
49 |
50 | if (config.DefaultAssemblyNamePatternToFindTypes is not null)
51 | builder.UseDefaultAssemblyNamePatternToFindTypes(config.DefaultAssemblyNamePatternToFindTypes);
52 |
53 | if (config.AssemblyNamePatternToFindComponentTypes is not null)
54 | builder.UseAssemblyNamePatternToFindComponentTypes(config.AssemblyNamePatternToFindComponentTypes);
55 |
56 | if (config.AssemblyNamePatternToFindAttributeTypes is not null)
57 | builder.UseAssemblyNamePatternToFindAttributeTypes(config.AssemblyNamePatternToFindAttributeTypes);
58 |
59 | if (config.AssemblyNamePatternToFindEventTypes is not null)
60 | builder.UseAssemblyNamePatternToFindEventTypes(config.AssemblyNamePatternToFindEventTypes);
61 |
62 | if (config.AssemblyNamePatternToFindEventHandlerTypes is not null)
63 | builder.UseAssemblyNamePatternToFindEventHandlerTypes(config.AssemblyNamePatternToFindEventHandlerTypes);
64 |
65 | Lazy lazyAssembliesToFindTypesIn = new Lazy(
66 | () => AssemblyFinder.FindAllByPattern(builder.BuildingContext.DefaultAssemblyNamePatternToFindTypes),
67 | isThreadSafe: false);
68 |
69 | if (config.AssertionExceptionType is not null)
70 | builder.UseAssertionExceptionType(
71 | TypeFinder.FindInAssemblies(config.AssertionExceptionType, lazyAssembliesToFindTypesIn.Value));
72 |
73 | if (config.AggregateAssertionExceptionType is not null)
74 | builder.UseAggregateAssertionExceptionType(
75 | TypeFinder.FindInAssemblies(config.AggregateAssertionExceptionType, lazyAssembliesToFindTypesIn.Value));
76 |
77 | if (config.AggregateAssertionStrategyType is not null)
78 | builder.UseAggregateAssertionStrategy(
79 | ActivatorEx.CreateInstance(
80 | TypeFinder.FindInAssemblies(config.AggregateAssertionStrategyType, lazyAssembliesToFindTypesIn.Value)));
81 |
82 | if (config.WarningReportStrategyType is not null)
83 | builder.UseWarningReportStrategy(
84 | ActivatorEx.CreateInstance(
85 | TypeFinder.FindInAssemblies(config.WarningReportStrategyType, lazyAssembliesToFindTypesIn.Value)));
86 |
87 | if (config.AssertionFailureReportStrategyType is not null)
88 | builder.UseAssertionFailureReportStrategy(
89 | ActivatorEx.CreateInstance(
90 | TypeFinder.FindInAssemblies(config.AssertionFailureReportStrategyType, lazyAssembliesToFindTypesIn.Value)));
91 |
92 | if (config.DomTestIdAttributeName is not null)
93 | builder.UseDomTestIdAttributeName(config.DomTestIdAttributeName);
94 |
95 | if (config.DomTestIdAttributeDefaultCase is not null)
96 | builder.UseDomTestIdAttributeDefaultCase(config.DomTestIdAttributeDefaultCase.Value);
97 |
98 | if (config.UseNUnitTestName)
99 | builder.UseNUnitTestName();
100 |
101 | if (config.UseNUnitTestSuiteName)
102 | builder.UseNUnitTestSuiteName();
103 |
104 | if (config.UseNUnitTestSuiteType)
105 | builder.UseNUnitTestSuiteType();
106 |
107 | List warnings = [];
108 |
109 | if (config.UseNUnitAggregateAssertionStrategy)
110 | builder.UseNUnitAggregateAssertionStrategy();
111 |
112 | if (config.UseNUnitWarningReportStrategy)
113 | builder.UseNUnitWarningReportStrategy();
114 |
115 | if (config.UseNUnitAssertionFailureReportStrategy)
116 | builder.UseNUnitAssertionFailureReportStrategy();
117 |
118 | if (config.UseAllNUnitFeatures)
119 | builder.UseAllNUnitFeatures();
120 |
121 | if (config.UseSpecFlowNUnitFeatures)
122 | builder.UseSpecFlowNUnitFeatures();
123 |
124 | if (config.LogConsumers is not null)
125 | {
126 | foreach (var item in config.LogConsumers)
127 | MapLogConsumer(item, builder);
128 | }
129 |
130 | if (config.Drivers is not null)
131 | {
132 | foreach (var item in config.Drivers)
133 | MapDriver(item, builder);
134 | }
135 |
136 | if (config.Attributes is not null)
137 | MapAttributes(config.Attributes, builder);
138 |
139 | if (config.EventSubscriptions is not null)
140 | MapEventSubscriptions(config.EventSubscriptions, builder);
141 |
142 | if (config.Screenshots is not null)
143 | MapScreenshots(config.Screenshots, builder);
144 |
145 | if (config.PageSnapshots is not null)
146 | MapPageSnapshots(config.PageSnapshots, builder);
147 |
148 | if (config.BrowserLogs is not null)
149 | MapBrowserLogs(config.BrowserLogs, builder);
150 |
151 | if (warnings.Count > 0)
152 | builder.EventSubscriptions.Add(new LogConfigurationWarningsEventHandler(warnings));
153 |
154 | return builder;
155 | }
156 |
157 | private static void MapLogConsumer(LogConsumerJsonSection section, AtataContextBuilder builder)
158 | {
159 | var consumerBuilder = builder.LogConsumers.Add(section.Type);
160 |
161 | if (section.MinLevel != null)
162 | consumerBuilder.WithMinLevel(section.MinLevel.Value);
163 |
164 | if (section.SectionEnd != null)
165 | consumerBuilder.WithSectionEnd(section.SectionEnd.Value);
166 |
167 | if (section.MessageNestingLevelIndent != null)
168 | consumerBuilder.WithMessageNestingLevelIndent(section.MessageNestingLevelIndent);
169 |
170 | if (section.MessageStartSectionPrefix != null)
171 | consumerBuilder.WithMessageStartSectionPrefix(section.MessageStartSectionPrefix);
172 |
173 | if (section.MessageEndSectionPrefix != null)
174 | consumerBuilder.WithMessageEndSectionPrefix(section.MessageEndSectionPrefix);
175 |
176 | consumerBuilder.WithProperties(section.ExtraPropertiesMap);
177 | }
178 |
179 | private static void MapDriver(DriverJsonSection section, AtataContextBuilder builder)
180 | {
181 | IDriverJsonMapper mapper = DriverJsonMapperAliases.Resolve(section.Type);
182 | mapper.Map(section, builder);
183 | }
184 |
185 | private static void MapAttributes(AttributesJsonSection attributesSection, AtataContextBuilder builder)
186 | {
187 | AttributeMapper attributeMapper = new AttributeMapper(
188 | builder.BuildingContext.AssemblyNamePatternToFindAttributeTypes ?? builder.BuildingContext.DefaultAssemblyNamePatternToFindTypes,
189 | builder.BuildingContext.DefaultAssemblyNamePatternToFindTypes);
190 |
191 | if (attributesSection.Global != null)
192 | {
193 | builder.Attributes.Global.Add(
194 | attributesSection.Global.Select(attributeMapper.Map));
195 | }
196 |
197 | if (attributesSection.Assembly != null)
198 | {
199 | foreach (AssemblyAttributesJsonSection assemblySection in attributesSection.Assembly)
200 | {
201 | if (string.IsNullOrEmpty(assemblySection.Name))
202 | throw new ConfigurationException(
203 | "\"name\" configuration property of assembly section is not specified.");
204 |
205 | if (assemblySection.Attributes != null)
206 | builder.Attributes.Assembly(assemblySection.Name).Add(
207 | assemblySection.Attributes.Select(attributeMapper.Map));
208 | }
209 | }
210 |
211 | if (attributesSection.Component != null)
212 | {
213 | foreach (ComponentAttributesJsonSection componentSection in attributesSection.Component)
214 | {
215 | if (string.IsNullOrEmpty(componentSection.Type))
216 | throw new ConfigurationException(
217 | "\"type\" configuration property of component section is not specified.");
218 |
219 | var componentAttributesBuilder = builder.Attributes.Component(componentSection.Type);
220 |
221 | if (componentSection.Attributes != null)
222 | componentAttributesBuilder.Add(componentSection.Attributes.Select(attributeMapper.Map));
223 |
224 | if (componentSection.Properties != null)
225 | {
226 | foreach (PropertyAttributesJsonSection propertySection in componentSection.Properties)
227 | {
228 | if (string.IsNullOrEmpty(propertySection.Name))
229 | throw new ConfigurationException(
230 | "\"name\" configuration property of property section is not specified.");
231 |
232 | if (propertySection.Attributes != null)
233 | componentAttributesBuilder.Property(propertySection.Name).Add(
234 | propertySection.Attributes.Select(attributeMapper.Map));
235 | }
236 | }
237 | }
238 | }
239 | }
240 |
241 | private static void MapEventSubscriptions(List sections, AtataContextBuilder builder)
242 | {
243 | EventSubscriptionMapper eventSubscriptionMapper = new EventSubscriptionMapper(
244 | builder.BuildingContext.AssemblyNamePatternToFindEventTypes ?? builder.BuildingContext.DefaultAssemblyNamePatternToFindTypes,
245 | builder.BuildingContext.AssemblyNamePatternToFindEventHandlerTypes ?? builder.BuildingContext.DefaultAssemblyNamePatternToFindTypes,
246 | builder.BuildingContext.DefaultAssemblyNamePatternToFindTypes);
247 |
248 | builder.BuildingContext.EventSubscriptions.AddRange(
249 | sections.Select(eventSubscriptionMapper.Map));
250 | }
251 |
252 | private static void MapScreenshots(ScreenshotsJsonSection section, AtataContextBuilder builder)
253 | {
254 | if (!string.IsNullOrEmpty(section.FileNameTemplate))
255 | builder.Screenshots.UseFileNameTemplate(section.FileNameTemplate);
256 |
257 | if (section.Strategy?.Type != null)
258 | {
259 | if (!ScreenshotStrategyAliases.TryResolve(section.Strategy.Type, out IScreenshotStrategy strategy))
260 | strategy = (IScreenshotStrategy)CreateObject(
261 | section.Strategy.Type,
262 | section.Strategy.ExtraPropertiesMap,
263 | builder.BuildingContext.DefaultAssemblyNamePatternToFindTypes);
264 |
265 | builder.Screenshots.UseStrategy(strategy);
266 | }
267 | }
268 |
269 | private static void MapPageSnapshots(PageSnapshotsJsonSection section, AtataContextBuilder builder)
270 | {
271 | if (!string.IsNullOrEmpty(section.FileNameTemplate))
272 | builder.PageSnapshots.UseFileNameTemplate(section.FileNameTemplate);
273 |
274 | if (section.Strategy?.Type != null)
275 | {
276 | if (!PageSnapshotStrategyAliases.TryResolve(section.Strategy.Type, out IPageSnapshotStrategy strategy))
277 | strategy = (IPageSnapshotStrategy)CreateObject(
278 | section.Strategy.Type,
279 | section.Strategy.ExtraPropertiesMap,
280 | builder.BuildingContext.DefaultAssemblyNamePatternToFindTypes);
281 |
282 | builder.PageSnapshots.UseStrategy(strategy);
283 | }
284 | }
285 |
286 | private static void MapBrowserLogs(BrowserLogsJsonSection section, AtataContextBuilder builder)
287 | {
288 | if (section.Log)
289 | builder.BrowserLogs.UseLog(section.Log);
290 |
291 | if (section.MinLevelOfWarning is not null)
292 | builder.BrowserLogs.UseMinLevelOfWarning(section.MinLevelOfWarning);
293 | }
294 |
295 | private static object CreateObject(string typeName, Dictionary valuesMap, string assemblyNamePatternToFindType)
296 | {
297 | ObjectConverter objectConverter = new()
298 | {
299 | AssemblyNamePatternToFindTypes = assemblyNamePatternToFindType
300 | };
301 | ObjectMapper objectMapper = new(objectConverter);
302 | ObjectCreator objectCreator = new(objectConverter, objectMapper);
303 |
304 | var assembliesToFindTypes = AssemblyFinder.FindAllByPattern(assemblyNamePatternToFindType);
305 | Type strategyType = TypeFinder.FindInAssemblies(typeName, assembliesToFindTypes);
306 | return objectCreator.Create(strategyType, valuesMap);
307 | }
308 | }
309 |
--------------------------------------------------------------------------------