agents-cli-automation 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,521 @@
1
+ name: Create Playwright Framework - UI Testing (C#)
2
+ description: Creates a production-ready Playwright UI automation framework in C# with .NET, NUnit, and all required configurations.
3
+ argument-hint: "framework requirements, e.g., 'C# with Playwright tests'"
4
+
5
+ ---
6
+
7
+ # Playwright UI Testing Framework - C#
8
+
9
+ This agent creates a production-ready Playwright framework in C# with .NET and NUnit optimized for UI automation.
10
+
11
+ ## Capabilities
12
+ - **.NET 8+** - Chromium only with modern C# patterns
13
+ - **NUnit** - Latest testing framework
14
+ - **C# 12** - Latest language features and async/await
15
+ - **Page Object Model (POM)** - Reusable page classes
16
+ - **Dependency Injection** - Built with NUnit setup
17
+ - **Parallel execution** - Tests run simultaneously
18
+ - **Test data support** - JSON, CSV, YAML loading
19
+ - **BDD Support** - SpecFlow with Gherkin syntax
20
+ - **Explicit waits** - Proper synchronization
21
+ - **Scalable folder structure** with best practices
22
+
23
+ ## Prerequisites
24
+ - .NET 8 SDK installed
25
+ - Visual Studio 2022 or VS Code with C# extension
26
+ - Git (optional)
27
+
28
+ ## Setup Instructions
29
+
30
+ ### 1. Create .NET Project
31
+ ```bash
32
+ dotnet new classlib -n PlaywrightFramework
33
+ cd PlaywrightFramework
34
+ dotnet new nunit --force
35
+ ```
36
+
37
+ ### 2. Update .csproj with Dependencies
38
+ ```xml
39
+ <Project Sdk="Microsoft.NET.Sdk">
40
+ <PropertyGroup>
41
+ <TargetFramework>net8.0</TargetFramework>
42
+ <ImplicitUsings>enable</ImplicitUsings>
43
+ <Nullable>enable</Nullable>
44
+ <LangVersion>latest</LangVersion>
45
+ </PropertyGroup>
46
+
47
+ <ItemGroup>
48
+ <PackageReference Include="Microsoft.Playwright" Version="1.50.0" />
49
+ <PackageReference Include="NUnit" Version="4.1.0" />
50
+ <PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
51
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.2" />
52
+
53
+ <!-- JSON -->
54
+ <PackageReference Include="System.Text.Json" Version="8.0.0" />
55
+
56
+ <!-- YAML -->
57
+ <PackageReference Include="YamlDotNet" Version="13.0.1" />
58
+
59
+ <!-- CSV -->
60
+ <PackageReference Include="CsvHelper" Version="30.0.1" />
61
+
62
+ <!-- SpecFlow (BDD) -->
63
+ <PackageReference Include="SpecFlow" Version="3.9.74" />
64
+ <PackageReference Include="SpecFlow.NUnit" Version="3.9.74" />
65
+ </ItemGroup>
66
+ </Project>
67
+ ```
68
+
69
+ ### 3. Project Structure
70
+ ```
71
+ PlaywrightFramework/
72
+ ├── Pages/
73
+ │ ├── BasePage.cs
74
+ │ ├── LoginPage.cs
75
+ │ ├── InventoryPage.cs
76
+ │ └── FormPage.cs
77
+ ├── Utils/
78
+ │ ├── DataReader.cs
79
+ │ ├── FormHelper.cs
80
+ │ └── TestDataManager.cs
81
+ ├── Models/
82
+ │ ├── User.cs
83
+ │ ├── FormData.cs
84
+ │ └── Config.cs
85
+ ├── Fixtures/
86
+ │ └── TestFixtures.cs
87
+ ├── Tests/
88
+ │ ├── SauceDemoTests.cs
89
+ │ └── FormTests.cs
90
+ ├── StepDefinitions/
91
+ │ ├── LoginSteps.cs
92
+ │ └── ShoppingSteps.cs
93
+ ├── Features/
94
+ │ ├── login.feature
95
+ │ └── shopping.feature
96
+ ├── Data/
97
+ │ ├── users.json
98
+ │ ├── formData.csv
99
+ │ └── config.yaml
100
+ ├── specflow.json
101
+ └── PlaywrightFramework.csproj
102
+ ```
103
+
104
+ ### 4. Base Page (Pages/BasePage.cs)
105
+ ```csharp
106
+ using Microsoft.Playwright;
107
+
108
+ namespace PlaywrightFramework.Pages
109
+ {
110
+ public class BasePage
111
+ {
112
+ protected IPage Page;
113
+ private const int Timeout = 5000;
114
+
115
+ public BasePage(IPage page)
116
+ {
117
+ Page = page;
118
+ }
119
+
120
+ public async Task NavigateToAsync(string url)
121
+ {
122
+ await Page.GotoAsync(url);
123
+ }
124
+
125
+ public async Task ClickAsync(string selector)
126
+ {
127
+ await Page.ClickAsync(selector);
128
+ }
129
+
130
+ public async Task FillAsync(string selector, string text)
131
+ {
132
+ await Page.FillAsync(selector, text);
133
+ }
134
+
135
+ public async Task<string?> GetTextAsync(string selector)
136
+ {
137
+ return await Page.TextContentAsync(selector);
138
+ }
139
+
140
+ public async Task WaitForElementAsync(string selector)
141
+ {
142
+ await Page.WaitForSelectorAsync(selector, new() { Timeout = Timeout });
143
+ }
144
+
145
+ public async Task<bool> IsVisibleAsync(string selector)
146
+ {
147
+ return await Page.Locator(selector).IsVisibleAsync();
148
+ }
149
+
150
+ public async Task SelectDropdownAsync(string selector, string optionText)
151
+ {
152
+ await Page.ClickAsync(selector);
153
+ await Page.ClickAsync($"text={optionText}");
154
+ }
155
+
156
+ public async Task SelectRadioButtonAsync(string labelText)
157
+ {
158
+ var label = Page.Locator($"label:has-text(\"{labelText}\")");
159
+ var radioId = await label.GetAttributeAsync("for");
160
+ await Page.ClickAsync($"#{radioId}");
161
+ }
162
+
163
+ public async Task CheckCheckboxAsync(string labelText)
164
+ {
165
+ var label = Page.Locator($"label:has-text(\"{labelText}\")");
166
+ var checkboxId = await label.GetAttributeAsync("for");
167
+ var checkbox = Page.Locator($"#{checkboxId}");
168
+ var isChecked = await checkbox.IsCheckedAsync();
169
+
170
+ if (!isChecked)
171
+ {
172
+ await checkbox.ClickAsync();
173
+ }
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### 5. Login Page (Pages/LoginPage.cs)
180
+ ```csharp
181
+ using Microsoft.Playwright;
182
+
183
+ namespace PlaywrightFramework.Pages
184
+ {
185
+ public class LoginPage : BasePage
186
+ {
187
+ private const string UsernameField = "[data-test=\"username\"]";
188
+ private const string PasswordField = "[data-test=\"password\"]";
189
+ private const string LoginButton = "[data-test=\"login-button\"]";
190
+ private const string AppUrl = "https://www.saucedemo.com/";
191
+
192
+ public LoginPage(IPage page) : base(page)
193
+ {
194
+ }
195
+
196
+ public async Task GoToAsync()
197
+ {
198
+ await Page.GotoAsync(AppUrl);
199
+ }
200
+
201
+ public async Task LoginAsync(string username, string password)
202
+ {
203
+ await Page.FillAsync(UsernameField, username);
204
+ await Page.FillAsync(PasswordField, password);
205
+ await Page.ClickAsync(LoginButton);
206
+ await Page.WaitForURLAsync("**/inventory.html");
207
+ }
208
+
209
+ public async Task<bool> IsLoadedAsync()
210
+ {
211
+ return await Page.Locator(UsernameField).IsVisibleAsync();
212
+ }
213
+ }
214
+ }
215
+ ```
216
+
217
+ ### 6. Data Reader (Utils/DataReader.cs)
218
+ ```csharp
219
+ using System.Text.Json;
220
+ using YamlDotNet.Serialization;
221
+ using CsvHelper;
222
+ using System.Globalization;
223
+
224
+ namespace PlaywrightFramework.Utils
225
+ {
226
+ public class DataReader
227
+ {
228
+ private static readonly string DataDir = Path.Combine(
229
+ AppContext.BaseDirectory,
230
+ "..", "..", "..", "Data"
231
+ );
232
+
233
+ public static async Task<Dictionary<string, object>> ReadJsonAsync(string filename)
234
+ {
235
+ var path = Path.Combine(DataDir, filename);
236
+ var content = await File.ReadAllTextAsync(path);
237
+ return JsonSerializer.Deserialize<Dictionary<string, object>>(content)
238
+ ?? new Dictionary<string, object>();
239
+ }
240
+
241
+ public static async Task<Dictionary<string, object>> ReadYamlAsync(string filename)
242
+ {
243
+ var path = Path.Combine(DataDir, filename);
244
+ var content = await File.ReadAllTextAsync(path);
245
+ var deserializer = new Deserializer();
246
+ return deserializer.Deserialize<Dictionary<string, object>>(content)
247
+ ?? new Dictionary<string, object>();
248
+ }
249
+
250
+ public static async Task<List<Dictionary<string, string>>> ReadCsvAsync(string filename)
251
+ {
252
+ var path = Path.Combine(DataDir, filename);
253
+ var records = new List<Dictionary<string, string>>();
254
+
255
+ using (var reader = new StreamReader(path))
256
+ using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
257
+ {
258
+ csv.Read();
259
+ csv.ReadHeader();
260
+
261
+ while (csv.Read())
262
+ {
263
+ var record = new Dictionary<string, string>();
264
+ foreach (var header in csv.HeaderRecord!)
265
+ {
266
+ record[header] = csv.GetField(header) ?? string.Empty;
267
+ }
268
+ records.Add(record);
269
+ }
270
+ }
271
+
272
+ return records;
273
+ }
274
+
275
+ public static Dictionary<string, string>? GetRecordByKey(
276
+ List<Dictionary<string, string>> data,
277
+ string key,
278
+ string value)
279
+ {
280
+ return data.FirstOrDefault(record =>
281
+ record.ContainsKey(key) && record[key] == value);
282
+ }
283
+ }
284
+ }
285
+ ```
286
+
287
+ ### 7. Form Helper (Utils/FormHelper.cs)
288
+ ```csharp
289
+ using Microsoft.Playwright;
290
+
291
+ namespace PlaywrightFramework.Utils
292
+ {
293
+ public class FormHelper
294
+ {
295
+ private readonly IPage _page;
296
+
297
+ public FormHelper(IPage page)
298
+ {
299
+ _page = page;
300
+ }
301
+
302
+ public async Task FillInputAsync(string selector, string value)
303
+ {
304
+ await _page.FillAsync(selector, value);
305
+ }
306
+
307
+ public async Task FillTextAreaAsync(string selector, string value)
308
+ {
309
+ var locator = _page.Locator(selector);
310
+ await locator.ClearAsync();
311
+ await locator.TypeAsync(value, new() { Delay = 50 });
312
+ }
313
+
314
+ public async Task SelectDropdownAsync(string dropdownSelector, string optionText)
315
+ {
316
+ await _page.ClickAsync(dropdownSelector);
317
+ await _page.ClickAsync($"text={optionText}");
318
+ }
319
+
320
+ public async Task SelectRadioButtonAsync(string labelText)
321
+ {
322
+ var label = _page.Locator($"label:has-text(\"{labelText}\")");
323
+ var radioId = await label.GetAttributeAsync("for");
324
+ await _page.ClickAsync($"#{radioId}");
325
+ }
326
+
327
+ public async Task CheckCheckboxAsync(string labelText)
328
+ {
329
+ var label = _page.Locator($"label:has-text(\"{labelText}\")");
330
+ var checkboxId = await label.GetAttributeAsync("for");
331
+ var checkbox = _page.Locator($"#{checkboxId}");
332
+
333
+ if (!await checkbox.IsCheckedAsync())
334
+ {
335
+ await checkbox.ClickAsync();
336
+ }
337
+ }
338
+
339
+ public async Task CheckMultipleAsync(List<string> labels)
340
+ {
341
+ foreach (var label in labels)
342
+ {
343
+ await CheckCheckboxAsync(label);
344
+ }
345
+ }
346
+
347
+ public async Task<string> GetInputValueAsync(string selector)
348
+ {
349
+ return await _page.InputValueAsync(selector);
350
+ }
351
+
352
+ public async Task<bool> IsCheckboxCheckedAsync(string labelText)
353
+ {
354
+ var label = _page.Locator($"label:has-text(\"{labelText}\")");
355
+ var checkboxId = await label.GetAttributeAsync("for");
356
+ return await _page.Locator($"#{checkboxId}").IsCheckedAsync();
357
+ }
358
+ }
359
+ }
360
+ ```
361
+
362
+ ### 8. Test Base Fixture (Fixtures/TestFixtures.cs)
363
+ ```csharp
364
+ using Microsoft.Playwright;
365
+
366
+ namespace PlaywrightFramework.Fixtures
367
+ {
368
+ public class TestFixtures
369
+ {
370
+ protected IPlaywright Playwright = null!;
371
+ protected IBrowser Browser = null!;
372
+ protected IBrowserContext Context = null!;
373
+ protected IPage Page = null!;
374
+
375
+ [SetUp]
376
+ public async Task SetupAsync()
377
+ {
378
+ Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
379
+ Browser = await Playwright.Chromium.LaunchAsync();
380
+ Context = await Browser.NewContextAsync();
381
+ Page = await Context.NewPageAsync();
382
+ }
383
+
384
+ [TearDown]
385
+ public async Task TeardownAsync()
386
+ {
387
+ if (Page != null)
388
+ await Page.CloseAsync();
389
+
390
+ if (Context != null)
391
+ await Context.CloseAsync();
392
+
393
+ if (Browser != null)
394
+ await Browser.CloseAsync();
395
+
396
+ if (Playwright != null)
397
+ Playwright.Dispose();
398
+ }
399
+ }
400
+ }
401
+ ```
402
+
403
+ ### 9. Sample Test (Tests/SauceDemoTests.cs)
404
+ ```csharp
405
+ using NUnit.Framework;
406
+ using PlaywrightFramework.Pages;
407
+ using PlaywrightFramework.Fixtures;
408
+
409
+ namespace PlaywrightFramework.Tests
410
+ {
411
+ [TestFixture]
412
+ public class SauceDemoTests : TestFixtures
413
+ {
414
+ [Test]
415
+ public async Task TestLoginWithValidCredentials()
416
+ {
417
+ var loginPage = new LoginPage(Page);
418
+ await loginPage.GoToAsync();
419
+ await loginPage.LoginAsync("standard_user", "secret_sauce");
420
+
421
+ Assert.That(Page.Url, Does.Contain("inventory.html"));
422
+ }
423
+
424
+ [Test]
425
+ public async Task TestLoginWithInvalidCredentials()
426
+ {
427
+ var loginPage = new LoginPage(Page);
428
+ await loginPage.GoToAsync();
429
+
430
+ await loginPage.LoginAsync("invalid_user", "wrong_password");
431
+
432
+ var errorMessage = await loginPage.GetTextAsync("[data-test=\"error\"]");
433
+ Assert.That(errorMessage, Does.Contain("Username and password do not match"));
434
+ }
435
+ }
436
+ }
437
+ ```
438
+
439
+ ### 10. Run Tests
440
+
441
+ ```bash
442
+ # Run all tests
443
+ dotnet test
444
+
445
+ # Run specific test class
446
+ dotnet test --filter=SauceDemoTests
447
+
448
+ # Run specific test method
449
+ dotnet test --filter="Name~TestLoginWithValidCredentials"
450
+
451
+ # Run with parallel execution (default)
452
+ dotnet test --parallel
453
+
454
+ # Run with NUnit console runner
455
+ nunit3-console PlaywrightFramework.csproj
456
+
457
+ # Run BDD scenarios
458
+ dotnet test --filter=BDD
459
+ ```
460
+
461
+ ### 11. Test Data Files
462
+
463
+ **Data/users.json**
464
+ ```json
465
+ {
466
+ "users": [
467
+ {
468
+ "id": "1",
469
+ "username": "standard_user",
470
+ "password": "secret_sauce",
471
+ "email": "user@example.com",
472
+ "firstName": "John",
473
+ "lastName": "Doe"
474
+ }
475
+ ]
476
+ }
477
+ ```
478
+
479
+ **Data/config.yaml**
480
+ ```yaml
481
+ app:
482
+ url: https://www.saucedemo.com/
483
+ timeout: 30000
484
+ users:
485
+ standard:
486
+ username: standard_user
487
+ password: secret_sauce
488
+ ```
489
+
490
+ **Data/formData.csv**
491
+ ```csv
492
+ formType,firstName,lastName,email
493
+ basic,John,Doe,john@example.com
494
+ extended,Jane,Smith,jane@example.com
495
+ ```
496
+
497
+ ### 12. specflow.json (BDD Configuration)
498
+ ```json
499
+ {
500
+ "language": {
501
+ "feature": "en"
502
+ },
503
+ "stepAssembly": "PlaywrightFramework",
504
+ "unitTestProvider": "nunit",
505
+ "bindingCulture": "en-US"
506
+ }
507
+ ```
508
+
509
+ ## Best Practices
510
+ ✅ **Page Object Model** - All selectors in page classes
511
+ ✅ **Async/Await** - Modern C# patterns throughout
512
+ ✅ **Dependency Injection** - NUnit fixtures
513
+ ✅ **Test Data** - Externalize in JSON, CSV, YAML
514
+ ✅ **Form Helpers** - Handle all form interactions
515
+ ✅ **Parallel Execution** - Default test runner behavior
516
+ ✅ **Chromium Only** - Single browser for consistency
517
+ ✅ **C# 12** - Latest language features
518
+ ✅ **NUnit** - Modern testing framework
519
+ ✅ **.NET 8** - Latest framework version
520
+
521
+ **Note:** This framework uses .NET 8, C# 12, Chromium only, and async/await patterns. Use `dotnet build` to compile and `dotnet test` to run tests with automatic parallel execution.