How to use EventBus class of Atata package

Best Atata code snippet using Atata.EventBus

AtataContext.cs

Source:AtataContext.cs Github

copy

Full Screen

...374375 /// <summary>376 /// Gets the event bus, which can used to subscribe to and publish events.377 /// </summary>378 public IEventBus EventBus { get; internal set; }379380 /// <summary>381 /// Gets the variables dictionary.382 /// <para>383 /// The list of predefined variables:384 /// <list type="bullet">385 /// <item><c>build-start</c></item>386 /// <item><c>build-start-utc</c></item>387 /// <item><c>basedir</c></item>388 /// <item><c>artifacts</c></item>389 /// <item><c>test-name-sanitized</c></item>390 /// <item><c>test-name</c></item>391 /// <item><c>test-suite-name-sanitized</c></item>392 /// <item><c>test-suite-name</c></item>393 /// <item><c>test-start</c></item>394 /// <item><c>test-start-utc</c></item>395 /// <item><c>driver-alias</c></item>396 /// </list>397 /// </para>398 /// <para>399 /// Custom variables can be added as well.400 /// </para>401 /// </summary>402 public IDictionary<string, object> Variables { get; } = new Dictionary<string, object>();403404 /// <summary>405 /// Creates <see cref="AtataContextBuilder"/> instance for <see cref="AtataContext"/> configuration.406 /// Sets the value to <see cref="AtataContextBuilder.BuildingContext"/> copied from <see cref="GlobalConfiguration"/>.407 /// </summary>408 /// <returns>The created <see cref="AtataContextBuilder"/> instance.</returns>409 public static AtataContextBuilder Configure()410 {411 AtataBuildingContext buildingContext = GlobalConfiguration.BuildingContext.Clone();412 return new AtataContextBuilder(buildingContext);413 }414415 internal void InitDateTimeProperties()416 {417 StartedAtUtc = DateTime.UtcNow;418 StartedAt = TimeZoneInfo.ConvertTimeFromUtc(StartedAtUtc, TimeZone);419420 if (BuildStartUtc is null)421 {422 lock (s_buildStartSyncLock)423 {424 if (BuildStartUtc is null)425 {426 BuildStartUtc = StartedAtUtc;427 BuildStart = BuildStartUtc.Value.ToLocalTime();428 }429 }430 }431432 BuildStartInTimeZone = TimeZoneInfo.ConvertTimeFromUtc(BuildStartUtc.Value, TimeZone);433 }434435 internal void InitMainVariables()436 {437 var variables = Variables;438439 variables["build-start"] = BuildStartInTimeZone;440 variables["build-start-utc"] = BuildStartUtc;441442 variables["basedir"] = AppDomain.CurrentDomain.BaseDirectory;443444 variables["test-name-sanitized"] = TestNameSanitized;445 variables["test-name"] = TestName;446 variables["test-suite-name-sanitized"] = TestSuiteNameSanitized;447 variables["test-suite-name"] = TestSuiteName;448 variables["test-start"] = StartedAt;449 variables["test-start-utc"] = StartedAtUtc;450451 variables["driver-alias"] = DriverAlias;452 }453454 internal void InitCustomVariables(IDictionary<string, object> customVariables)455 {456 var variables = Variables;457458 foreach (var variable in customVariables)459 variables[variable.Key] = variable.Value;460 }461462 internal void InitArtifactsVariable() =>463 Variables["artifacts"] = Artifacts.FullName.Value;464465 internal void LogTestStart()466 {467 StringBuilder logMessageBuilder = new StringBuilder(468 $"Starting {GetTestUnitKindName()}");469470 string[] testFullNameParts = GetTestFullNameParts().ToArray();471472 if (testFullNameParts.Length > 0)473 {474 logMessageBuilder.Append(": ")475 .Append(string.Join(".", testFullNameParts));476 }477478 Log.Info(logMessageBuilder.ToString());479 }480481 private IEnumerable<string> GetTestFullNameParts()482 {483 if (TestSuiteType != null)484 yield return TestSuiteType.Namespace;485486 if (TestSuiteName != null)487 yield return TestSuiteName;488489 if (TestName != null)490 yield return TestName;491 }492493 private string GetTestUnitKindName()494 {495 return TestName != null496 ? "test"497 : TestSuiteType != null498 ? "test suite"499 : "test unit";500 }501502 /// <summary>503 /// Executes aggregate assertion using <see cref="AggregateAssertionStrategy" />.504 /// </summary>505 /// <param name="action">The action to execute in scope of aggregate assertion.</param>506 /// <param name="assertionScopeName">507 /// Name of the scope being asserted (page object, control, etc.).508 /// Is used to identify the assertion section in log.509 /// Can be null.510 /// </param>511 public void AggregateAssert(Action action, string assertionScopeName = null)512 {513 action.CheckNotNull(nameof(action));514515 AggregateAssertionStrategy.Assert(() =>516 {517 AggregateAssertionLevel++;518519 try520 {521 Log.ExecuteSection(522 new AggregateAssertionLogSection(assertionScopeName),523 action);524 }525 finally526 {527 AggregateAssertionLevel--;528 }529 });530 }531532 /// <summary>533 /// Cleans up the test context.534 /// </summary>535 /// <param name="quitDriver">if set to <see langword="true"/> quits WebDriver.</param>536 public void CleanUp(bool quitDriver = true)537 {538 if (_disposed)539 return;540541 PureExecutionStopwatch.Stop();542543 Log.ExecuteSection(544 new LogSection("Clean up AtataContext", LogLevel.Trace),545 () =>546 {547 EventBus.Publish(new AtataContextCleanUpEvent(this));548549 CleanUpTemporarilyPreservedPageObjectList();550551 if (PageObject != null)552 UIComponentResolver.CleanUpPageObject(PageObject);553554 UIComponentAccessChainScopeCache.Release();555556 if (quitDriver)557 _driver?.Dispose();558 });559560 ExecutionStopwatch.Stop();561562 string testUnitKindName = GetTestUnitKindName();563 Log.InfoWithExecutionTimeInBrackets($"Finished {testUnitKindName}", ExecutionStopwatch.Elapsed);564 Log.InfoWithExecutionTime($"Pure {testUnitKindName} execution time:", PureExecutionStopwatch.Elapsed);565566 Log = null;567568 if (Current == this)569 Current = null;570571 _disposed = true;572573 AssertionResults.Clear();574575 if (PendingFailureAssertionResults.Any())576 {577 var copyOfPendingFailureAssertionResults = PendingFailureAssertionResults.ToArray();578 PendingFailureAssertionResults.Clear();579580 throw VerificationUtils.CreateAggregateAssertionException(copyOfPendingFailureAssertionResults);581 }582 }583584 internal void InitDriver()585 {586 if (DriverFactory is null)587 throw new InvalidOperationException(588 $"Failed to create an instance of {typeof(IWebDriver).FullName} as driver factory is not specified.");589590 _driver = DriverFactory.Create()591 ?? throw new InvalidOperationException(592 $"Failed to create an instance of {typeof(IWebDriver).FullName} as driver factory returned null as a driver.");593594 _driver.Manage().Timeouts().SetRetryTimeout(ElementFindTimeout, ElementFindRetryInterval);595596 EventBus.Publish(new DriverInitEvent(_driver));597 }598599 /// <summary>600 /// Restarts the driver.601 /// </summary>602 public void RestartDriver()603 {604 Log.ExecuteSection(605 new LogSection("Restart driver"),606 () =>607 {608 CleanUpTemporarilyPreservedPageObjectList();609610 if (PageObject != null) ...

Full Screen

Full Screen

EventBusTests.cs

Source:EventBusTests.cs Github

copy

Full Screen

2using Moq;3using NUnit.Framework;4namespace Atata.Tests5{6 public class EventBusTests7 {8 protected Subject<EventBus> Sut { get; private set; }9 protected AtataContext Context { get; private set; }10 [SetUp]11 public void SetUp()12 {13 Context = AtataContext.Configure()14 .UseDriverInitializationStage(AtataContextDriverInitializationStage.None)15 .Build();16 Sut = new EventBus(Context)17 .ToSutSubject();18 }19 [TestFixture]20 public class Publish : EventBusTests21 {22 [Test]23 public void Null() =>24 Sut.Invoking(x => x.Publish<TestEvent>(null))25 .Should.Throw<ArgumentNullException>();26 [Test]27 public void WhenThereIsNoSubscription() =>28 Sut.Invoking(x => x.Publish(new TestEvent()))29 .Should.Not.Throw();30 [Test]31 public void WhenThereIsSubscription()32 {33 var actionMock = new Mock<Action<TestEvent>>();34 var eventData = new TestEvent();35 Sut.Object.Subscribe(actionMock.Object);36 Sut.Act(x => x.Publish(eventData));37 actionMock.Verify(x => x(eventData), Times.Once);38 }39 [Test]40 public void WhenThereIsSubscription_CanHandle_False()41 {42 var conditionalEventHandlerMock = new Mock<IConditionalEventHandler<TestEvent>>(MockBehavior.Strict);43 var eventData = new TestEvent();44 Sut.Object.Subscribe(conditionalEventHandlerMock.Object);45 conditionalEventHandlerMock.Setup(x => x.CanHandle(eventData, Context)).Returns(false);46 Sut.Act(x => x.Publish(eventData));47 }48 [Test]49 public void WhenThereIsSubscription_CanHandle_True()50 {51 var conditionalEventHandlerMock = new Mock<IConditionalEventHandler<TestEvent>>(MockBehavior.Strict);52 var eventData = new TestEvent();53 Sut.Object.Subscribe(conditionalEventHandlerMock.Object);54 conditionalEventHandlerMock.Setup(x => x.CanHandle(eventData, Context)).Returns(true);55 conditionalEventHandlerMock.Setup(x => x.Handle(eventData, Context));56 Sut.Act(x => x.Publish(eventData));57 }58 [Test]59 public void WhenThereAreMultipleSubscriptions()60 {61 var actionMock1 = new Mock<Action<TestEvent>>(MockBehavior.Strict);62 var actionMock2 = new Mock<Action<TestEvent, AtataContext>>(MockBehavior.Strict);63 var eventHandlerMock1 = new Mock<IConditionalEventHandler<TestEvent>>(MockBehavior.Strict);64 var eventHandlerMock2 = new Mock<IEventHandler<TestEvent>>(MockBehavior.Strict);65 var eventData = new TestEvent();66 Sut.Object.Subscribe(actionMock1.Object);67 Sut.Object.Subscribe(actionMock2.Object);68 Sut.Object.Subscribe(eventHandlerMock1.Object);69 Sut.Object.Subscribe(eventHandlerMock2.Object);70 MockSequence sequence = new MockSequence();71 actionMock1.InSequence(sequence).Setup(x => x(eventData));72 actionMock2.InSequence(sequence).Setup(x => x(eventData, Context));73 eventHandlerMock1.InSequence(sequence).Setup(x => x.CanHandle(eventData, Context)).Returns(true);74 eventHandlerMock1.InSequence(sequence).Setup(x => x.Handle(eventData, Context));75 eventHandlerMock2.InSequence(sequence).Setup(x => x.Handle(eventData, Context));76 Sut.Act(x => x.Publish(eventData));77 }78 [Test]79 public void AfterUnsubscribe()80 {81 var actionMock1 = new Mock<Action<TestEvent>>();82 var actionMock2 = new Mock<Action<TestEvent, AtataContext>>();83 var eventData = new TestEvent();84 var subscription1 = Sut.Object.Subscribe(actionMock1.Object);85 Sut.Object.Subscribe(actionMock2.Object);86 Sut.Object.Unsubscribe(subscription1);87 Sut.Act(x => x.Publish(eventData));88 actionMock1.Verify(x => x(eventData), Times.Never);89 actionMock2.Verify(x => x(eventData, Context), Times.Once);90 }91 [Test]92 public void AfterUnsubscribeHandler()93 {94 var actionMock = new Mock<Action<TestEvent>>();95 var eventHandlerMock = new Mock<IEventHandler<TestEvent>>();96 var eventData = new TestEvent();97 Sut.Object.Subscribe(actionMock.Object);98 Sut.Object.Subscribe(eventHandlerMock.Object);99 Sut.Object.UnsubscribeHandler(eventHandlerMock.Object);100 Sut.Act(x => x.Publish(eventData));101 actionMock.Verify(x => x(eventData), Times.Once);102 eventHandlerMock.Verify(x => x.Handle(eventData, Context), Times.Never);103 }104 [Test]105 public void AfterUnsubscribeAll()106 {107 var actionMock1 = new Mock<Action<TestEvent>>();108 var actionMock2 = new Mock<Action<TestEvent, AtataContext>>();109 var eventData = new TestEvent();110 Sut.Object.Subscribe(actionMock1.Object);111 Sut.Object.Subscribe(actionMock2.Object);112 Sut.Object.UnsubscribeAll<TestEvent>();113 Sut.Act(x => x.Publish(eventData));114 actionMock1.Verify(x => x(eventData), Times.Never);115 actionMock2.Verify(x => x(eventData, Context), Times.Never);116 }117 }118 [TestFixture]119 public class Subscribe : EventBusTests120 {121 [Test]122 public void Action_Null() =>123 Sut.Invoking(x => x.Subscribe<TestEvent>(null as Action))124 .Should.Throw<ArgumentNullException>();125 [Test]126 public void Action()127 {128 var actionMock = new Mock<Action<TestEvent>>();129 Sut.ResultOf(x => x.Subscribe(actionMock.Object))130 .Should.Not.BeNull();131 }132 }133 [TestFixture]134 public class Unsubscribe : EventBusTests135 {136 [Test]137 public void Null() =>138 Sut.Invoking(x => x.Unsubscribe(null))139 .Should.Throw<ArgumentNullException>();140 [Test]141 public void Valid()142 {143 var actionMock = new Mock<Action<TestEvent>>();144 var subscription = Sut.Object.Subscribe(actionMock.Object);145 Sut.Invoking(x => x.Unsubscribe(subscription))146 .Should.Not.Throw();147 }148 [Test]149 public void Twice()150 {151 var actionMock = new Mock<Action<TestEvent>>();152 var subscription = Sut.Object.Subscribe(actionMock.Object);153 Sut.Act(x => x.Unsubscribe(subscription));154 Sut.Invoking(x => x.Unsubscribe(subscription))155 .Should.Not.Throw();156 }157 }158 [TestFixture]159 public class UnsubscribeHandler : EventBusTests160 {161 [Test]162 public void Null() =>163 Sut.Invoking(x => x.UnsubscribeHandler(null))164 .Should.Throw<ArgumentNullException>();165 [Test]166 public void Valid()167 {168 var actionMock = new Mock<IEventHandler<TestEvent>>();169 Sut.Object.Subscribe(actionMock.Object);170 Sut.Invoking(x => x.UnsubscribeHandler(actionMock.Object))171 .Should.Not.Throw();172 }173 [Test]...

Full Screen

Full Screen

EventBus.cs

Source:EventBus.cs Github

copy

Full Screen

...6{7 /// <summary>8 /// Represents the event bus, which provides a functionality of subscribing to and publishing events.9 /// </summary>10 public class EventBus : IEventBus11 {12 private readonly AtataContext _context;13 private readonly ConcurrentDictionary<Type, List<EventHandlerSubscription>> _subscriptionMap = new ConcurrentDictionary<Type, List<EventHandlerSubscription>>();14 /// <summary>15 /// Initializes a new instance of the <see cref="EventBus"/> class.16 /// </summary>17 /// <param name="context">The context.</param>18 public EventBus(AtataContext context)19 : this(context, null)20 {21 }22 internal EventBus(AtataContext context, IEnumerable<EventSubscriptionItem> eventSubscriptions)23 {24 _context = context.CheckNotNull(nameof(context));25 if (eventSubscriptions != null)26 foreach (var subscription in eventSubscriptions)27 Subscribe(subscription.EventType, subscription.EventHandler);28 }29 /// <inheritdoc/>30 public void Publish<TEvent>(TEvent eventData)31 {32 eventData.CheckNotNull(nameof(eventData));33 if (_subscriptionMap.TryGetValue(typeof(TEvent), out var eventHandlerSubscriptions))34 {35 object[] eventHandlersArray;36 lock (eventHandlerSubscriptions)...

Full Screen

Full Screen

EventBus

Using AI Code Generation

copy

Full Screen

1using Atata;2using NUnit.Framework;3{4 {5 public void SetUp()6 {7 Build();8 }9 public void Test1()10 {11 LoginForm.Submit();12 }13 public void Test2()14 {15 LoginForm.Submit();16 }17 public void TearDown()18 {19 AtataContext.Current.CleanUp();20 }21 }22}23using Atata;24using NUnit.Framework;25{26 {27 public void SetUp()28 {29 Build();30 }31 public void Test1()32 {33 LoginForm.Submit();34 }

Full Screen

Full Screen

EventBus

Using AI Code Generation

copy

Full Screen

1using Atata;2using NUnit.Framework;3{4 {5 public void _2()6 {7 Build();8 using (AtataContext.Begin())9 {10 LogOut.ClickAndGo();11 }12 }13 }14}15using Atata;16using NUnit.Framework;17{18 {19 public void _3()20 {21 Build();22 using (AtataContext.Begin())23 {24 LogOut.ClickAndGo();25 }26 }27 }28}29using Atata;30using NUnit.Framework;31{32 {33 public void _4()34 {35 Build();36 using (AtataContext.Begin())37 {38 LogOut.ClickAndGo();39 }40 }41 }42}43using Atata;44using NUnit.Framework;45{46 {47 public void _5()48 {

Full Screen

Full Screen

EventBus

Using AI Code Generation

copy

Full Screen

1using Atata;2using NUnit.Framework;3using System;4using System.Collections.Generic;5using System.Linq;6using System.Text;7using System.Threading.Tasks;8{9 {10 public void SetUp()11 {12 Build();13 }14 public void TestMethod()15 {16 Results[x => x.Text.Contains("Atata")].Should.Exist();17 }18 public void TearDown()19 {20 AtataContext.Current?.CleanUp();21 }22 }23}24using Atata;25using NUnit.Framework;26using System;27using System.Collections.Generic;28using System.Linq;29using System.Text;30using System.Threading.Tasks;31{32 {33 public void SetUp()34 {35 Build();36 }37 public void TestMethod()38 {39 Results[x => x.Text.Contains("Atata")].Should.Exist();40 }41 public void TearDown()42 {43 AtataContext.Current?.CleanUp();44 }45 }46}47using Atata;48using NUnit.Framework;49using System;50using System.Collections.Generic;51using System.Linq;52using System.Text;53using System.Threading.Tasks;54{55 {56 public void SetUp()57 {

Full Screen

Full Screen

EventBus

Using AI Code Generation

copy

Full Screen

1using Atata;2using NUnit.Framework;3{4 {5 public void SetUp()6 {7 AtataContext.Configure()8 .UseChrome()9 .UseCulture("en-us")10 .UseAllNUnitFeatures()11 .Build();12 }13 public void TearDown()14 {15 AtataContext.Current?.CleanUp();16 }17 public void Test()18 {19 Go.To<GooglePage>().Search("Atata");20 }21 }22}23using Atata;24using NUnit.Framework;25{26 {27 public void SetUp()28 {29 AtataContext.Configure()30 .UseChrome()31 .UseCulture("en-us")32 .UseAllNUnitFeatures()33 .Build();34 }35 public void TearDown()36 {37 AtataContext.Current?.CleanUp();38 }39 public void Test()40 {41 Go.To<GooglePage>().Search("Atata");42 }43 }44}45using Atata;46using NUnit.Framework;47{48 {49 public void SetUp()50 {51 AtataContext.Configure()52 .UseChrome()53 .UseCulture("en-us")54 .UseAllNUnitFeatures()55 .Build();56 }57 public void TearDown()58 {59 AtataContext.Current?.CleanUp();60 }61 public void Test()62 {63 Go.To<GooglePage>().Search("Atata");64 }65 }66}67using Atata;68using NUnit.Framework;69{70 {71 public void SetUp()72 {73 AtataContext.Configure()

Full Screen

Full Screen

EventBus

Using AI Code Generation

copy

Full Screen

1using Atata;2using NUnit.Framework;3{4 {5 public void _2()6 {7 Build();8 using (AtataContext.Begin())9 {10 Footer.Should.Equal("© 2019 Atata Sample App");11 }12 }13 }14}15using Atata;16using NUnit.Framework;17{18 {19 public void _3()20 {21 Build();22 using (AtataContext.Begin())23 {24 Header.Should.Equal("Terms of Use");25 }26 }27 }28}29using Atata;30using NUnit.Framework;31{32 {33 public void _4()34 {35 Build();36 using (AtataContext.Begin())37 {38 Header.Should.Equal("Welcome to Atata Sample App!");39 }40 }41 }42}43using Atata;44using NUnit.Framework;

Full Screen

Full Screen

EventBus

Using AI Code Generation

copy

Full Screen

1using Atata;2{3 {4 public void _2()5 {6 Go.To<HomePage>()7 .SignIn.ClickAndGo()8 .Email.Set("

Full Screen

Full Screen

EventBus

Using AI Code Generation

copy

Full Screen

1using Atata;2{3 {4 public void Test()5 {6 Go.To<HomePage>()7 .SignIn.ClickAndGo()8 .Login.Set("John")9 .Password.Set("123")10 .SignIn.ClickAndGo<HomePage>()11 .AssertThat(x => x.UserName.Should.Equal("John"));12 }13 }14}15using Atata;16{17 {18 public void Test()19 {20 Go.To<HomePage>()21 .SignIn.ClickAndGo()22 .Login.Set("John")23 .Password.Set("123")24 .SignIn.ClickAndGo<HomePage>()25 .AssertThat(x => x.UserName.Should.Equal("John"))26 .LogOut.ClickAndGo();27 }28 }29}30using Atata;31{32 {33 public void Test()34 {35 Go.To<HomePage>()36 .SignIn.ClickAndGo()37 .Login.Set("John")38 .Password.Set("123")39 .SignIn.ClickAndGo<HomePage>()40 .AssertThat(x => x.UserName.Should.Equal("John"))41 .LogOut.ClickAndGo()42 .SignIn.ClickAndGo()43 .Login.Set("Jane")44 .Password.Set("456")45 .SignIn.ClickAndGo<HomePage>()46 .AssertThat(x => x.UserName.Should.Equal("Jane"));47 }48 }49}50using Atata;51{52 {53 public void Test()54 {55 Go.To<HomePage>()56 .SignIn.ClickAndGo()57 .Login.Set("John")58 .Password.Set("123")59 .SignIn.ClickAndGo<HomePage>()60 .AssertThat(x => x.UserName.Should.Equal("John"))61 .LogOut.ClickAndGo()62 .SignIn.ClickAndGo()63 .Login.Set("Jane")64 .Password.Set("456")65 .SignIn.ClickAndGo<HomePage>()

Full Screen

Full Screen

EventBus

Using AI Code Generation

copy

Full Screen

1{2 public void Test()3 {4 using (var app = AtataContext.Configure().UseChrome().Build())5 {6 app.LogInToApplication("username", "password");7 app.GoTo<HomePage>().Logout.Click();8 }9 }10}11{12 public void Test()13 {14 using (var app = AtataContext.Configure().UseChrome().Build())15 {16 app.LogInToApplication("username", "password");17 app.GoTo<HomePage>().Logout.Click();18 }19 }20}21{22 public void Test()23 {24 using (var app = AtataContext.Configure().UseChrome().Build())25 {26 app.LogInToApplication("username", "password");27 app.GoTo<HomePage>().Logout.Click();28 }29 }30}31{32 public void Test()33 {34 using (var app = AtataContext.Configure().UseChrome().Build())35 {36 app.LogInToApplication("username", "password");37 app.GoTo<HomePage>().Logout.Click();38 }39 }40}41{42 public void Test()43 {44 using (var app = AtataContext.Configure().UseChrome().Build())45 {46 app.LogInToApplication("username", "password");47 app.GoTo<HomePage>().Logout.Click();48 }49 }50}51{52 public void Test()53 {54 using (var app = AtataContext.Configure().UseChrome().Build())55 {56 app.LogInToApplication("username", "password");57 app.GoTo<HomePage>().Logout.Click();58 }59 }60}61{62 public void Test()63 {

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Atata automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful