├── .gitignore
├── LICENSE
├── README.md
├── docs
├── standards-and-practices
│ └── standards-and-conventions.md
└── technical-guides
│ └── deploy-q2a-azure-vm.md
└── src
├── app
├── Dovs.Q2AAutoKit.Suite.sln
└── Dovs.Q2AAutoKit
│ ├── App.config
│ ├── Common
│ └── ElementIds.cs
│ ├── Dovs.Q2AAutoKit.csproj
│ ├── Interfaces
│ ├── IConfigurationService.cs
│ ├── IExcelReaderService.cs
│ ├── IPasswordService.cs
│ ├── IUserManagementService.cs
│ └── IWebDriverService.cs
│ ├── Program.cs
│ ├── Services
│ ├── ConfigurationService.cs
│ ├── ExcelReaderService.cs
│ ├── PasswordService.cs
│ ├── UserManagementService.cs
│ └── WebDriverService.cs
│ └── UserData.cs
└── iac
└── ..txt
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.tlog
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
298 | *.vbp
299 |
300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
301 | *.dsw
302 | *.dsp
303 |
304 | # Visual Studio 6 technical files
305 | *.ncb
306 | *.aps
307 |
308 | # Visual Studio LightSwitch build output
309 | **/*.HTMLClient/GeneratedArtifacts
310 | **/*.DesktopClient/GeneratedArtifacts
311 | **/*.DesktopClient/ModelManifest.xml
312 | **/*.Server/GeneratedArtifacts
313 | **/*.Server/ModelManifest.xml
314 | _Pvt_Extensions
315 |
316 | # Paket dependency manager
317 | .paket/paket.exe
318 | paket-files/
319 |
320 | # FAKE - F# Make
321 | .fake/
322 |
323 | # CodeRush personal settings
324 | .cr/personal
325 |
326 | # Python Tools for Visual Studio (PTVS)
327 | __pycache__/
328 | *.pyc
329 |
330 | # Cake - Uncomment if you are using it
331 | # tools/**
332 | # !tools/packages.config
333 |
334 | # Tabs Studio
335 | *.tss
336 |
337 | # Telerik's JustMock configuration file
338 | *.jmconfig
339 |
340 | # BizTalk build output
341 | *.btp.cs
342 | *.btm.cs
343 | *.odx.cs
344 | *.xsd.cs
345 |
346 | # OpenCover UI analysis results
347 | OpenCover/
348 |
349 | # Azure Stream Analytics local run output
350 | ASALocalRun/
351 |
352 | # MSBuild Binary and Structured Log
353 | *.binlog
354 |
355 | # NVidia Nsight GPU debugger configuration file
356 | *.nvuser
357 |
358 | # MFractors (Xamarin productivity tool) working folder
359 | .mfractor/
360 |
361 | # Local History for Visual Studio
362 | .localhistory/
363 |
364 | # Visual Studio History (VSHistory) files
365 | .vshistory/
366 |
367 | # BeatPulse healthcheck temp database
368 | healthchecksdb
369 |
370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
371 | MigrationBackup/
372 |
373 | # Ionide (cross platform F# VS Code tools) working folder
374 | .ionide/
375 |
376 | # Fody - auto-generated XML schema
377 | FodyWeavers.xsd
378 |
379 | # VS Code files for those working on multiple tools
380 | .vscode/*
381 | !.vscode/settings.json
382 | !.vscode/tasks.json
383 | !.vscode/launch.json
384 | !.vscode/extensions.json
385 | *.code-workspace
386 |
387 | # Local History for Visual Studio Code
388 | .history/
389 |
390 | # Windows Installer files from build outputs
391 | *.cab
392 | *.msi
393 | *.msix
394 | *.msm
395 | *.msp
396 |
397 | # JetBrains Rider
398 | *.sln.iml
399 |
400 | # Ignore build results and temporary files for Dovs.FileSystemInteractor
401 | src/app/Dovs.FileSystemInteractor/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 DevOpsVisions
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Question2Answer Automation Toolkit
2 |
3 | This project automates several activities for the Question2Answer platform reducing manual effort and errors.
4 | It utilizes Selenium WebDriver to interact with the platform, performing tasks such as the following features:
5 | ## Features
6 | - **User Registration:** Automates the registration process through the registration page.
7 | - **Removing Users:** Automates the process of deleting users from the platform.
8 | - **Reads User Data:** Extracts user information, such as username and email, directly from an Excel file, streamlining the registration process.
9 | - **Secure Password Handling:** Ensures that passwords are handled securely during the registration process, protecting sensitive user information.
10 | - **Flexible File Path Support:** Supports both default and custom file paths for the Excel file, allowing users to easily manage and update user data sources.
11 | - **Detailed Logging:** Logs the entire registration process, tracking each step and identifying any errors that occur for easy troubleshooting.
12 |
13 | ## Prerequisites
14 |
15 | - [.NET Core SDK](https://dotnet.microsoft.com/download)
16 | - [Selenium WebDriver](https://www.selenium.dev/) (ChromeDriver)
17 | - [ExcelDataReader](https://github.com/ExcelDataReader/ExcelDataReader) library for reading Excel files.
18 | - Google Chrome browser installed.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/docs/standards-and-practices/standards-and-conventions.md:
--------------------------------------------------------------------------------
1 | # Standards and Conventions
2 | All standards and conventions outlined here are based on our established guidelines, which can be found in the following repository:
3 |
4 | [Common Workspace Hub](https://github.com/DevOpsVisions/common-workspace-hub)
5 |
6 | ## Repository and Application
7 |
8 | - **Repository Name**: `question2answer-automation-toolkit`
9 | - **Application Name**: `Q2A AutoKit`
10 | - **Executable Name**: `Q2AAutoKit`
11 |
12 | ## Solution and Projects
13 |
14 | - **Solution File**: `Dovs.Q2AAutoKit.Suite`
15 | - **Project Files**:
16 | - `Dovs.Q2AAutoKit`: The main project file for the core application.
17 | - `Dovs.Q2AAutoKit.UnitTest`: The project file dedicated to unit tests.
18 | - `Dovs.Q2AAutoKit.IntTest`: The project file dedicated to integration tests.
19 |
20 | ## Azure Cloud
21 | **Testing Environment:**
22 | - **Resource Name**: `rg-q2a-test-uksouth-001`
23 | - **Workload/Application**: Question2Answer
24 | - **Environment**: Test
25 | - **Azure Region**: UK South
26 | - **Instance**: 001
27 |
28 | ## Release Notes (Example)
29 |
30 | |
**Aug 06**
**[@Mohamed Radwan]()**
**[20240514.1]()**
**[ce53bd8]()**
|**Q2A AutoKit 1.6.1**
[DevOpsVisions]() released this 2 weeks ago [v1.6.1]() [2c537d3]()
1.6.1 (06-08-2024)
**Features Added:**
- Added a new read-only property ([Issue #345](), [PR #254]())
**Bugs Fixed:**
- First row MS Excel error ([Issue #123](), [PR #234]())
**Assets**
[App (zip)]() 06 August
[Source code (zip)]() 06 August
|
31 | | :- | :- |
--------------------------------------------------------------------------------
/docs/technical-guides/deploy-q2a-azure-vm.md:
--------------------------------------------------------------------------------
1 | # Deploying or Restoring Question2Answer on an Azure VM
2 |
3 | This guide provides a comprehensive walkthrough for setting up a testing environment on Azure by deploying or restoring Question2Answer on an Ubuntu VM. The instructions cover preparing a LAMP stack environment, configuring essential components, and deploying the Question2Answer application. Follow these steps to ensure a smooth deployment and configuration process for testing purposes.
4 |
5 | ## 1. Preparing a LAMP Stack Environment
6 |
7 | To prepare your environment, refer to the following guide:
8 |
9 | [Preparing a LAMP Stack Environment for Hosting](https://github.com/DevOpsVisions/common-workspace-hub/blob/main/docs/technical-guides/prepare-lamp-stack-env.md)
10 |
11 | ## 2. Download and Set Up Question2Answer
12 |
13 | Download the Question2Answer zip file, whether it's the latest version or a backup from your production environment.
14 |
15 | - **Unzip the Question2Answer Archive:** Extract the downloaded Question2Answer zip file.
16 |
17 | ```bash
18 | unzip /home/azureuser/Downloads/question2answer-latest.zip
19 | ```
20 |
21 | - **Clean the Web Directory:** Remove any existing files in the Apache web directory to ensure a clean installation.
22 |
23 | ```bash
24 | sudo rm -r /var/www/html/*
25 | ```
26 |
27 | - **Copy Question2Answer to the Web Directory:** Copy the extracted Question2Answer files to the Apache web directory.
28 |
29 | ```bash
30 | sudo cp -r /home/azureuser/Downloads/question2answer-1.8.8/public_html/. /var/www/html
31 | ```
32 |
33 | - **Set Ownership and Permissions:** Adjust the ownership and permissions to ensure the files are accessible by the web server.
34 |
35 | ```bash
36 | sudo chown -R www-data:www-data /var/www/html/
37 | ```
38 | ```bash
39 | sudo chmod -R 755 /var/www/html/
40 | ```
41 |
42 | ## 3. Configure MySQL for Question2Answer
43 |
44 | - **Log in to MySQL:** Log in to MySQL with the root user.
45 |
46 | ```bash
47 | sudo mysql -p
48 | ```
49 | *This command opens the MySQL command line interface. The `-p` option prompts you to enter your MySQL root password.*
50 |
51 | - **List all MySQL users:**
52 |
53 | ```sql
54 | SELECT User, Host FROM mysql.user;
55 | ```
56 | *This SQL query displays all existing users and their host permissions in the MySQL server.*
57 |
58 | - **Create a new MySQL user for Question2Answer:**
59 |
60 | ```sql
61 | CREATE USER 'q2aUser'@'localhost' IDENTIFIED BY 'password';
62 | ```
63 | *Creates a new MySQL user `q2aUser` with the password `password`, restricted to the local host.*
64 |
65 | > [!IMPORTANT]
66 | > Ensure you use a secure password.
67 |
68 | - **Check existing databases:**
69 |
70 | ```sql
71 | SHOW DATABASES;
72 | ```
73 | *Lists all databases on the MySQL server to ensure there is no conflict with the new database name.*
74 |
75 | - **Create a new Question2Answer database:**
76 |
77 | ```sql
78 | CREATE DATABASE question2answer;
79 | ```
80 | ```sql
81 | SHOW DATABASES;
82 | ```
83 | *Creates a new database named `question2answer` and lists all databases to confirm its creation.*
84 |
85 | - **Grant all privileges to the new user:**
86 |
87 | ```sql
88 | GRANT ALL PRIVILEGES ON question2answer.* TO 'q2aUser'@'localhost';
89 | ```
90 | ```sql
91 | FLUSH PRIVILEGES;
92 | ```
93 | *Grants full privileges to `q2aUser` on the `question2answer` database and applies the changes with `FLUSH PRIVILEGES`.*
94 |
95 | ## 4. Import Database (Optional)
96 |
97 | If you are restoring from a production environment backup, use the following commands to import the database:
98 |
99 | ```sql
100 | USE question2answer;
101 | ```
102 | ```sql
103 | source /home/azureuser/Downloads/oldbackup.sql
104 | ```
105 |
106 | ## 5. Configure Question2Answer
107 |
108 | - **Edit Configuration File**: Set up the qa-config.php file to connect Question2Answer to your MySQL database.
109 |
110 | If you're using a new installation of Question2Answer, start by copying the sample configuration file to create a working configuration file. If you're restoring from a backup, the qa-config.php file should already exist; simply edit it as needed.
111 |
112 | **For a New Installation:**
113 |
114 | ```bash
115 | sudo cp qa-config-example.php qa-config.php
116 | ```
117 | **Edit the Configuration File:**
118 |
119 | ```bash
120 | sudo nano qa-config.php
121 | ```
122 | In the qa-config.php file, update the following settings with your database information:
123 |
124 | - **Database Name:** Replace 'your-database-name' with the name of the database you created.
125 | - **Database User:** Replace 'your-database-username' with the username.
126 | - **Database Password:** Replace 'your-database-password' with the password.
127 |
128 | Ensure these settings match the credentials you configured during the MySQL setup. Save and close the file when you're done.
129 |
130 | ## 6. Final Checks and Testing
131 |
132 | After completing these steps, navigate to `http://` in a web browser to access your Question2Answer installation. If restored from a backup, the application should display the restored version.
133 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit.Suite.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.11.35219.272
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dovs.Q2AAutoKit", "Dovs.Q2AAutoKit\Dovs.Q2AAutoKit.csproj", "{BCC115BE-DDEC-438B-96F5-8AD8989CAA3F}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dovs.FileSystemInteractor", "Dovs.FileSystemInteractor\Dovs.FileSystemInteractor.csproj", "{FA430FA5-D1D4-41EC-8738-09A0982089B0}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {BCC115BE-DDEC-438B-96F5-8AD8989CAA3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {BCC115BE-DDEC-438B-96F5-8AD8989CAA3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {BCC115BE-DDEC-438B-96F5-8AD8989CAA3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {BCC115BE-DDEC-438B-96F5-8AD8989CAA3F}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {FA430FA5-D1D4-41EC-8738-09A0982089B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {FA430FA5-D1D4-41EC-8738-09A0982089B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {FA430FA5-D1D4-41EC-8738-09A0982089B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {FA430FA5-D1D4-41EC-8738-09A0982089B0}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {82C7BE0E-4760-427E-BCB1-25964185DD49}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Common/ElementIds.cs:
--------------------------------------------------------------------------------
1 | namespace Dovs.Q2AAutoKit.Common
2 | {
3 | ///
4 | /// The ElementIds class serves as a centralized location for storing commonly used HTML element IDs
5 | /// and CSS selectors within the application. By using constants for these values, we can avoid hardcoding
6 | /// strings directly in the code, improving maintainability and reducing errors.
7 | ///
8 | public class ElementIds
9 | {
10 | ///
11 | /// The HTML element ID for the username (handle) field in the registration form.
12 | ///
13 | public const string HANDLE = "handle";
14 |
15 | ///
16 | /// The HTML element ID for the password field in the registration form.
17 | ///
18 | public const string PASSWORD = "password";
19 |
20 | ///
21 | /// The HTML element ID for the email field in the registration form.
22 | ///
23 | public const string EMAIL = "email";
24 |
25 | ///
26 | /// The CSS selector used to locate the registration button in the form.
27 | /// This selector targets the button with the specific value attribute of 'Register'.
28 | ///
29 | public const string SELECTOR_REGISTER = ".qa-form-tall-button-register[value='Register']";
30 |
31 | ///
32 | /// The CSS selector used to locate the error element in the form.
33 | /// This selector targets elements with the class 'qa-error'.
34 | ///
35 | public const string ERROR = ".qa-error";
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Dovs.Q2AAutoKit.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Interfaces/IConfigurationService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Dovs.Q2AAutoKit.Interfaces
8 | {
9 | public interface IConfigurationService
10 | {
11 | string GetConfigValue(string key);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Interfaces/IExcelReaderService.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Dovs.Q2AAutoKit.Interfaces
4 | {
5 | // Interface to abstract Excel reading functionality
6 | public interface IExcelReaderService
7 | {
8 | List ReadUserData(string filePath);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Interfaces/IPasswordService.cs:
--------------------------------------------------------------------------------
1 | namespace Dovs.Q2AAutoKit.Interfaces
2 | {
3 | // Interface to abstract password reading functionality
4 | public interface IPasswordService
5 | {
6 | string PromptForPassword(string prompt);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Interfaces/IUserManagementService.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Dovs.Q2AAutoKit.Interfaces
4 | {
5 | // Interface to abstract the user registration process
6 | public interface IUserManagementService
7 | {
8 | bool RegisterUsers(List users, string password);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Interfaces/IWebDriverService.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium;
2 |
3 | namespace Dovs.Q2AAutoKit.Interfaces
4 | {
5 | // Interface to abstract WebDriver creation to allow flexibility in using different browsers
6 | public interface IWebDriverService
7 | {
8 | IWebDriver CreateWebDriver();
9 | void QuitWebDriver(IWebDriver driver);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Program.cs:
--------------------------------------------------------------------------------
1 | using Dovs.FileSystemInteractor.Interfaces;
2 | using Dovs.FileSystemInteractor.Services;
3 | using Dovs.Q2AAutoKit;
4 | using Dovs.Q2AAutoKit.Common;
5 | using Dovs.Q2AAutoKit.Interfaces;
6 | using Dovs.Q2AAutoKit.Services;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.IO;
10 |
11 | class Program
12 | {
13 | static void Main(string[] args)
14 | {
15 | DisplayMenu();
16 | }
17 |
18 | static void DisplayMenu()
19 | {
20 | Console.WriteLine("Welcome to Q2A Automation Toolkit!");
21 | Console.WriteLine("Please select an option:");
22 | Console.WriteLine("1. Register Users");
23 | Console.WriteLine("2. Remove Users");
24 | Console.WriteLine("3. Update Users");
25 | Console.WriteLine("4. Exit");
26 |
27 | int option = GetOptionFromUser();
28 |
29 | switch (option)
30 | {
31 | case 1:
32 | RegisterUsers();
33 | DisplayMenu();
34 | break;
35 | case 2:
36 | RemoveUsers();
37 | DisplayMenu();
38 | break;
39 | case 3:
40 | UpdateUsers();
41 | DisplayMenu();
42 | break;
43 | case 4:
44 | Console.WriteLine("Exiting the App...");
45 | break;
46 | default:
47 | Console.WriteLine("Invalid option. Please try again.");
48 | DisplayMenu();
49 | break;
50 | }
51 | }
52 |
53 | static int GetOptionFromUser()
54 | {
55 | Console.Write("Enter the option number: ");
56 | string input = Console.ReadLine();
57 | int option;
58 | if (int.TryParse(input, out option))
59 | {
60 | return option;
61 | }
62 | else
63 | {
64 | Console.WriteLine("Invalid option. Please try again.");
65 | return GetOptionFromUser();
66 | }
67 | }
68 |
69 | static void RegisterUsers()
70 | {
71 | const int LEVELSTRAVERSE = 2;
72 |
73 | IFilePathService filePathService = new FilePathService();
74 | IConfigurationService configurationService = new ConfigurationService();
75 | IFileInteractionService fileInteractionService = new FileInteractionService();
76 |
77 | string filePath = fileInteractionService.SelectFilePath(filePathService, LEVELSTRAVERSE);
78 |
79 | if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
80 | {
81 | Console.ForegroundColor = ConsoleColor.Red;
82 | Console.WriteLine("File not found. Please provide a valid file path.");
83 | Console.ResetColor();
84 | return;
85 | }
86 |
87 | IPasswordService passwordService = new PasswordService();
88 | string password = passwordService.PromptForPassword("Enter the password to use for registration:");
89 |
90 | IExcelReaderService excelReader = new ExcelReaderService(configurationService);
91 | List userDataList = excelReader.ReadUserData(filePath);
92 |
93 | IWebDriverService webDriverService = new WebDriverService();
94 | IUserManagementService registrationService = new UserManagementService(webDriverService, configurationService);
95 |
96 | bool allUsersRegistered = registrationService.RegisterUsers(userDataList, password);
97 |
98 | if (allUsersRegistered)
99 | {
100 | Console.ForegroundColor = ConsoleColor.Green;
101 | Console.WriteLine("All users have been registered successfully.");
102 | }
103 | else
104 | {
105 | Console.ForegroundColor = ConsoleColor.Red;
106 | Console.WriteLine("Registration process encountered errors.");
107 | }
108 | Console.ResetColor();
109 | }
110 |
111 | static void RemoveUsers()
112 | {
113 | // TODO: Implement the logic to remove users
114 | Console.WriteLine("Remove Users method is not implemented yet.");
115 | }
116 |
117 | static void UpdateUsers()
118 | {
119 | // TODO: Implement the logic to update users
120 | Console.WriteLine("Update Users method is not implemented yet.");
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Services/ConfigurationService.cs:
--------------------------------------------------------------------------------
1 | using Dovs.Q2AAutoKit.Interfaces;
2 | using System.Configuration;
3 |
4 | namespace Dovs.Q2AAutoKit.Services
5 | {
6 | public class ConfigurationService : IConfigurationService
7 | {
8 | public string GetConfigValue(string key)
9 | {
10 | return ConfigurationManager.AppSettings[key];
11 | }
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Services/ExcelReaderService.cs:
--------------------------------------------------------------------------------
1 | using ExcelDataReader;
2 | using Dovs.Q2AAutoKit.Interfaces;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 |
7 | namespace Dovs.Q2AAutoKit.Services
8 | {
9 | public class ExcelReaderService : IExcelReaderService
10 | {
11 | private readonly IConfigurationService _configurationService;
12 |
13 | public ExcelReaderService(IConfigurationService configurationService)
14 | {
15 | _configurationService = configurationService;
16 | }
17 |
18 | public List ReadUserData(string filePath)
19 | {
20 | List userDataList = new List();
21 | System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
22 |
23 | // Read column names from the configuration service
24 | string userNameColumn = _configurationService.GetConfigValue("UserNameColumn");
25 | string emailColumn = _configurationService.GetConfigValue("EmailColumn");
26 |
27 | using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read))
28 | using (var reader = ExcelReaderFactory.CreateReader(stream))
29 | {
30 | var header = new Dictionary();
31 | bool isHeaderProcessed = false;
32 |
33 | while (reader.Read())
34 | {
35 | if (!isHeaderProcessed)
36 | {
37 | for (int i = 0; i < reader.FieldCount; i++)
38 | {
39 | header[reader.GetValue(i).ToString()] = i;
40 | }
41 |
42 | if (!header.ContainsKey(userNameColumn) || !header.ContainsKey(emailColumn))
43 | {
44 | throw new Exception($"Required columns '{userNameColumn}' or '{emailColumn}' not found.");
45 | }
46 |
47 | isHeaderProcessed = true;
48 | continue;
49 | }
50 |
51 | string userName = reader.GetValue(header[userNameColumn])?.ToString();
52 | string email = reader.GetValue(header[emailColumn])?.ToString();
53 |
54 | if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(email))
55 | {
56 | userDataList.Add(new UserData(userName, email));
57 | }
58 | }
59 | }
60 |
61 | if (userDataList.Count == 0)
62 | {
63 | throw new Exception("No user data found in Excel.");
64 | }
65 |
66 | return userDataList;
67 | }
68 | }
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Services/PasswordService.cs:
--------------------------------------------------------------------------------
1 | using Dovs.Q2AAutoKit.Interfaces;
2 | using System;
3 |
4 | namespace Dovs.Q2AAutoKit.Services
5 | {
6 | public class PasswordService : IPasswordService
7 | {
8 | public string PromptForPassword(string prompt)
9 | {
10 | Console.WriteLine(prompt);
11 | return ReadPassword();
12 | }
13 |
14 | private string ReadPassword()
15 | {
16 | string password = string.Empty;
17 | ConsoleKeyInfo key;
18 |
19 | // Loop to read each character entered by the user
20 | do
21 | {
22 | key = Console.ReadKey(intercept: true); // Read key without displaying it
23 |
24 | // Handle backspace to allow for correction of the password
25 | if (key.Key == ConsoleKey.Backspace && password.Length > 0)
26 | {
27 | password = password.Substring(0, password.Length - 1);
28 | Console.Write("\b \b"); // Remove last character from console
29 | }
30 | else if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
31 | {
32 | // Append the character to the password and mask it with '*'
33 | password += key.KeyChar;
34 | Console.Write("*");
35 | }
36 | }
37 | while (key.Key != ConsoleKey.Enter); // Stop when the user presses Enter
38 |
39 | Console.WriteLine(); // Move to the next line after password input is complete
40 | return password; // Return the password entered by the user
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Services/UserManagementService.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium;
2 | using Dovs.Q2AAutoKit.Common;
3 | using Dovs.Q2AAutoKit.Interfaces;
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | namespace Dovs.Q2AAutoKit.Services
8 | {
9 | // Class responsible for user registration process
10 | public class UserManagementService : IUserManagementService
11 | {
12 | private readonly IWebDriverService _webDriverService;
13 | private readonly IConfigurationService _configurationService;
14 |
15 | public UserManagementService(IWebDriverService webDriverService, IConfigurationService configurationService)
16 | {
17 | _webDriverService = webDriverService;
18 | _configurationService = configurationService;
19 | }
20 |
21 | public bool RegisterUsers(List users, string password)
22 | {
23 | using (var driver = _webDriverService.CreateWebDriver())
24 | {
25 | string registrationUrl = _configurationService.GetConfigValue("RegistrationUrl");
26 | bool isFirstIteration = true;
27 |
28 | foreach (var user in users)
29 | {
30 | Console.WriteLine($"Registering user: {user.UserName}, {user.Email}");
31 | driver.Navigate().GoToUrl(registrationUrl);
32 |
33 | FillRegistrationForm(driver, user, password);
34 | ClickRegisterButton(driver);
35 | System.Threading.Thread.Sleep(1000);
36 |
37 | if (isFirstIteration && IsErrorElementPresent(driver))
38 | {
39 | Console.ForegroundColor = ConsoleColor.Red;
40 | Console.WriteLine("Error detected, retrying registration...");
41 | Console.ResetColor();
42 | ClickRegisterButton(driver);
43 | System.Threading.Thread.Sleep(1000);
44 | isFirstIteration = false; // Set the flag to false after the first iteration
45 | }
46 |
47 | Logout(driver);
48 |
49 | if (!IsRegistrationFormPresent(driver))
50 | {
51 | Console.ForegroundColor = ConsoleColor.Green;
52 | Console.WriteLine("User registered successfully.");
53 | Console.ResetColor();
54 | }
55 | else
56 | {
57 | Console.ForegroundColor = ConsoleColor.Red;
58 | Console.WriteLine("Failed to register user.");
59 | Console.ResetColor();
60 | return false;
61 | }
62 | }
63 |
64 | return true;
65 | }
66 | }
67 |
68 | private bool IsErrorElementPresent(IWebDriver driver)
69 | {
70 | return ElementExists(driver, By.CssSelector(ElementIds.ERROR));
71 | }
72 |
73 | private void FillRegistrationForm(IWebDriver driver, UserData userData, string password)
74 | {
75 | driver.FindElement(By.Id(ElementIds.HANDLE)).SendKeys(userData.UserName);
76 | driver.FindElement(By.Id(ElementIds.PASSWORD)).SendKeys(password);
77 | driver.FindElement(By.Id(ElementIds.EMAIL)).SendKeys(userData.Email);
78 | }
79 |
80 | private void ClickRegisterButton(IWebDriver driver)
81 | {
82 | ClickElement(driver, By.CssSelector(ElementIds.SELECTOR_REGISTER), "Registration button not found; form may have already been submitted.");
83 | }
84 |
85 | private void Logout(IWebDriver driver)
86 | {
87 | string logoutUrl = _configurationService.GetConfigValue("LogoutUrl");
88 | driver.Navigate().GoToUrl(logoutUrl);
89 | }
90 |
91 | private bool IsRegistrationFormPresent(IWebDriver driver)
92 | {
93 | return ElementExists(driver, By.Id(ElementIds.HANDLE));
94 | }
95 |
96 | private bool ElementExists(IWebDriver driver, By by)
97 | {
98 | try
99 | {
100 | driver.FindElement(by);
101 | return true;
102 | }
103 | catch (NoSuchElementException)
104 | {
105 | return false;
106 | }
107 | }
108 |
109 | private void ClickElement(IWebDriver driver, By by, string errorMessage)
110 | {
111 | try
112 | {
113 | driver.FindElement(by).Click();
114 | }
115 | catch (NoSuchElementException)
116 | {
117 | Console.ForegroundColor = ConsoleColor.Red;
118 | Console.WriteLine(errorMessage);
119 | Console.ResetColor();
120 | }
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/Services/WebDriverService.cs:
--------------------------------------------------------------------------------
1 | using OpenQA.Selenium.Chrome;
2 | using OpenQA.Selenium;
3 | using Dovs.Q2AAutoKit.Interfaces;
4 |
5 | namespace Dovs.Q2AAutoKit.Services
6 | {
7 | // Class responsible for creating instances of WebDriver (currently using ChromeDriver)
8 | public class WebDriverService : IWebDriverService
9 | {
10 | ///
11 | /// Creates and returns an instance of Chrome WebDriver for automated browser actions.
12 | ///
13 | /// An instance of IWebDriver using ChromeDriver.
14 | public IWebDriver CreateWebDriver()
15 | {
16 | return new ChromeDriver();
17 | }
18 |
19 | public void QuitWebDriver(IWebDriver driver)
20 | {
21 | driver.Quit();
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/Dovs.Q2AAutoKit/UserData.cs:
--------------------------------------------------------------------------------
1 | namespace Dovs.Q2AAutoKit
2 | {
3 | public class UserData
4 | {
5 | public string UserName { get; }
6 | public string Email { get; }
7 |
8 | public UserData(string userName, string email)
9 | {
10 | UserName = userName;
11 | Email = email;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/iac/..txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevOpsVisions/question2answer-automation-toolkit/f67db0518a7d4dcf712b79ced02c0a1bea5f107f/src/iac/..txt
--------------------------------------------------------------------------------