├── .gitignore ├── CC.AutomatedTesting.Cmd.sln ├── CC.AutomatedTesting.Demo ├── App.config ├── CC.AutomatedTesting.Demo.csproj ├── ChromeDriver与Chrome版本对应.txt ├── ProcessControl.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── ReadME.txt ├── Reports │ └── MyOwnReport.html ├── bitbug_favicon.ico ├── packages.config ├── 使用时注意事项.txt └── 脚本 │ └── 流程1 │ └── 贴吧登录.txt ├── CC.AutomatedTesting.Extends.Cloud ├── CC.AutomatedTesting.Extends.Cloud.csproj ├── LoginTiebaAction.cs ├── Properties │ └── AssemblyInfo.cs ├── Random.cs ├── SearchAction.cs ├── TableList │ └── AssertTableExistLinkText.cs ├── TestAssert1Action.cs ├── TestAssert2Action.cs ├── ThreadSleepAction.cs ├── app.config └── packages.config ├── CC.AutomatedTesting.Infrastructure.Tests ├── CC.AutomatedTesting.Infrastructure.Tests.csproj ├── Properties │ └── AssemblyInfo.cs ├── TextAnalyzer │ └── AnalyzerTests.cs ├── UserFunction │ └── FunctionTest.cs ├── VersionControl │ └── VersionHelperTests.cs ├── app.config └── 脚本 │ └── 函数1.txt ├── CC.AutomatedTesting.Infrastructure ├── ActionFactory │ ├── AssertActionFactory.cs │ ├── FuncActionFactory.cs │ ├── IActionFactory.cs │ ├── JsOrHtmlActionContainer.cs │ ├── PerformActionFactory.cs │ └── TypeContainer.cs ├── Bizbases │ ├── Action.cs │ ├── ActionDescription.cs │ ├── ActionMethodAttribute.cs │ ├── AssertAction.cs │ ├── CommonActions │ │ ├── HtmlAssertAction.cs │ │ ├── HtmlPerfromAction.cs │ │ ├── JsAssertAction.cs │ │ └── JsPerfromAction.cs │ ├── FuncAction.cs │ ├── FunctionAction.cs │ ├── IAssert.cs │ ├── IPerform.cs │ ├── IPreconditionCheck.cs │ ├── PerformAction.cs │ └── VersionControlAttribute.cs ├── CC.AutomatedTesting.Infrastructure.csproj ├── Common │ ├── DictionaryExtensions.cs │ ├── RetryHelper.cs │ └── ValueConverter.cs ├── Constants.cs ├── ConstructingContext.cs ├── Exceptions │ └── BizException.cs ├── Extensions │ ├── StringEx.cs │ ├── WebDriverEx.cs │ ├── WebDriverWaitEx.cs │ └── WebElementEx.cs ├── FunctionProcessor │ └── FuncProcessor.cs ├── Jquery │ └── JQueryExecutor.cs ├── Log │ └── Computer.cs ├── ParallelRun │ └── TPLManager.cs ├── Properties │ └── AssemblyInfo.cs ├── TestCase.cs ├── TestCaseExecuter.cs ├── TestCaseFactory.cs ├── TestSuite.cs ├── TestSuiteFactory.cs ├── TestSuiteRunner.cs ├── TextAnalyzer │ ├── Analyzer.cs │ └── Escaper.cs ├── WebDriverFactory.cs ├── app.config └── packages.config ├── CC.AutomatedTesting.Reporting ├── CC.AutomatedTesting.Reporting.csproj ├── ExtentReport.cs ├── IReport.cs ├── Properties │ └── AssemblyInfo.cs ├── ReportFactory.cs └── packages.config ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | _OpenAPIReferences/ 3 | _ExternalReferences/ 4 | /MSBuild 5 | TestResults/ 6 | obj/ 7 | *.bat 8 | *.dll 9 | *.zip 10 | *.rar 11 | *.lib 12 | *.cache 13 | *.bak 14 | *.mdf 15 | *.ldf 16 | *.pch 17 | *.pdb 18 | *.suo 19 | *.user 20 | *.userosscache 21 | *.sln.docstates 22 | *.userprefs 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | build/ 30 | bld/ 31 | [Bb]in/ 32 | [Oo]bj/ 33 | .vs/ 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | *.VisualState.xml 37 | TestResult.xml 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | project.lock.json 42 | artifacts/ 43 | *_i.c 44 | *_p.c 45 | *_i.h 46 | *.ilk 47 | *.meta 48 | *.obj 49 | *.pdb 50 | *.pgc 51 | *.pgd 52 | *.rsp 53 | *.sbr 54 | *.tlb 55 | *.tli 56 | *.tlh 57 | *.tmp 58 | *.tmp_proj 59 | *.log 60 | *.vspscc 61 | *.vssscc 62 | .builds 63 | *.pidb 64 | *.svclog 65 | *.scc 66 | _Chutzpah* 67 | ipch/ 68 | *.aps 69 | *.ncb 70 | *.opensdf 71 | *.sdf 72 | *.cachefile 73 | *.psess 74 | *.vsp 75 | *.vspx 76 | $tf/ 77 | *.gpState 78 | _ReSharper*/ 79 | *.[Rr]e[Ss]harper 80 | *.DotSettings.user 81 | .JustCode 82 | _TeamCity* 83 | *.dotCover 84 | _NCrunch_* 85 | .*crunch*.local.xml 86 | *.mm.* 87 | AutoTest.Net/ 88 | .sass-cache/ 89 | [Ee]xpress/ 90 | DocProject/buildhelp/ 91 | DocProject/Help/*.HxT 92 | DocProject/Help/*.HxC 93 | DocProject/Help/*.hhc 94 | DocProject/Help/*.hhk 95 | DocProject/Help/*.hhp 96 | DocProject/Help/Html2 97 | DocProject/Help/html 98 | publish/ 99 | *.[Pp]ublish.xml 100 | *.azurePubxml 101 | *.publishproj 102 | *.nupkg 103 | **/packages/* 104 | !**/packages/build/ 105 | csx/ 106 | *.build.csdef 107 | AppPackages/ 108 | *.[Cc]ache 109 | !*.[Cc]ache/ 110 | ClientBin/ 111 | [Ss]tyle[Cc]op.* 112 | ~$* 113 | *~ 114 | *.dbmdl 115 | *.dbproj.schemaview 116 | *.pfx 117 | *.publishsettings 118 | node_modules/ 119 | orleans.codegen.cs 120 | Generated_Code/ 121 | _UpgradeReport_Files/ 122 | Backup*/ 123 | UpgradeLog*.XML 124 | UpgradeLog*.htm 125 | #*.rdl.data 126 | *.bim.layout 127 | *.bim_*.settings 128 | FakesAssemblies/ 129 | .ntvs_analysis.dat 130 | *.plg 131 | *.opt 132 | GeneratedArtifacts/ 133 | _Pvt_Extensions/ 134 | ModelManifest.xml 135 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Cmd.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CC.AutomatedTesting.Demo", "CC.AutomatedTesting.Demo\CC.AutomatedTesting.Demo.csproj", "{980F233F-33BA-4683-B561-AC88A02D68BD}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CC.AutomatedTesting.Infrastructure", "CC.AutomatedTesting.Infrastructure\CC.AutomatedTesting.Infrastructure.csproj", "{81FBDF32-88AB-4FB5-8AF5-6A6D7C045214}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CC.AutomatedTesting.Extends.Cloud", "CC.AutomatedTesting.Extends.Cloud\CC.AutomatedTesting.Extends.Cloud.csproj", "{3E3537E1-431C-4EC7-A092-631B8C1BCB23}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CC.AutomatedTesting.Reporting", "CC.AutomatedTesting.Reporting\CC.AutomatedTesting.Reporting.csproj", "{379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CC.AutomatedTesting.Infrastructure.Tests", "CC.AutomatedTesting.Infrastructure.Tests\CC.AutomatedTesting.Infrastructure.Tests.csproj", "{114A7D59-793C-4AF0-AA17-0424C13DA9C2}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Debug|x64 = Debug|x64 20 | Release|Any CPU = Release|Any CPU 21 | Release|x64 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {980F233F-33BA-4683-B561-AC88A02D68BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {980F233F-33BA-4683-B561-AC88A02D68BD}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {980F233F-33BA-4683-B561-AC88A02D68BD}.Debug|x64.ActiveCfg = Debug|Any CPU 27 | {980F233F-33BA-4683-B561-AC88A02D68BD}.Debug|x64.Build.0 = Debug|Any CPU 28 | {980F233F-33BA-4683-B561-AC88A02D68BD}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {980F233F-33BA-4683-B561-AC88A02D68BD}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {980F233F-33BA-4683-B561-AC88A02D68BD}.Release|x64.ActiveCfg = Release|Any CPU 31 | {980F233F-33BA-4683-B561-AC88A02D68BD}.Release|x64.Build.0 = Release|Any CPU 32 | {81FBDF32-88AB-4FB5-8AF5-6A6D7C045214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {81FBDF32-88AB-4FB5-8AF5-6A6D7C045214}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {81FBDF32-88AB-4FB5-8AF5-6A6D7C045214}.Debug|x64.ActiveCfg = Debug|x64 35 | {81FBDF32-88AB-4FB5-8AF5-6A6D7C045214}.Debug|x64.Build.0 = Debug|x64 36 | {81FBDF32-88AB-4FB5-8AF5-6A6D7C045214}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {81FBDF32-88AB-4FB5-8AF5-6A6D7C045214}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {81FBDF32-88AB-4FB5-8AF5-6A6D7C045214}.Release|x64.ActiveCfg = Release|x64 39 | {81FBDF32-88AB-4FB5-8AF5-6A6D7C045214}.Release|x64.Build.0 = Release|x64 40 | {3E3537E1-431C-4EC7-A092-631B8C1BCB23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {3E3537E1-431C-4EC7-A092-631B8C1BCB23}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {3E3537E1-431C-4EC7-A092-631B8C1BCB23}.Debug|x64.ActiveCfg = Debug|x64 43 | {3E3537E1-431C-4EC7-A092-631B8C1BCB23}.Debug|x64.Build.0 = Debug|x64 44 | {3E3537E1-431C-4EC7-A092-631B8C1BCB23}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {3E3537E1-431C-4EC7-A092-631B8C1BCB23}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {3E3537E1-431C-4EC7-A092-631B8C1BCB23}.Release|x64.ActiveCfg = Release|x64 47 | {3E3537E1-431C-4EC7-A092-631B8C1BCB23}.Release|x64.Build.0 = Release|x64 48 | {379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88}.Debug|x64.ActiveCfg = Debug|x64 51 | {379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88}.Debug|x64.Build.0 = Debug|x64 52 | {379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88}.Release|x64.ActiveCfg = Release|x64 55 | {379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88}.Release|x64.Build.0 = Release|x64 56 | {114A7D59-793C-4AF0-AA17-0424C13DA9C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {114A7D59-793C-4AF0-AA17-0424C13DA9C2}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {114A7D59-793C-4AF0-AA17-0424C13DA9C2}.Debug|x64.ActiveCfg = Debug|x64 59 | {114A7D59-793C-4AF0-AA17-0424C13DA9C2}.Debug|x64.Build.0 = Debug|x64 60 | {114A7D59-793C-4AF0-AA17-0424C13DA9C2}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {114A7D59-793C-4AF0-AA17-0424C13DA9C2}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {114A7D59-793C-4AF0-AA17-0424C13DA9C2}.Release|x64.ActiveCfg = Release|x64 63 | {114A7D59-793C-4AF0-AA17-0424C13DA9C2}.Release|x64.Build.0 = Release|x64 64 | {DE6DF8FA-C3B9-4B73-9EBF-6F6711610953}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {DE6DF8FA-C3B9-4B73-9EBF-6F6711610953}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {DE6DF8FA-C3B9-4B73-9EBF-6F6711610953}.Debug|x64.ActiveCfg = Debug|x64 67 | {DE6DF8FA-C3B9-4B73-9EBF-6F6711610953}.Debug|x64.Build.0 = Debug|x64 68 | {DE6DF8FA-C3B9-4B73-9EBF-6F6711610953}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {DE6DF8FA-C3B9-4B73-9EBF-6F6711610953}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {DE6DF8FA-C3B9-4B73-9EBF-6F6711610953}.Release|x64.ActiveCfg = Release|x64 71 | {DE6DF8FA-C3B9-4B73-9EBF-6F6711610953}.Release|x64.Build.0 = Release|x64 72 | EndGlobalSection 73 | GlobalSection(SolutionProperties) = preSolution 74 | HideSolutionNode = FALSE 75 | EndGlobalSection 76 | GlobalSection(ExtensibilityGlobals) = postSolution 77 | SolutionGuid = {6301F369-7122-45FD-AE66-AE0F37F7683F} 78 | EndGlobalSection 79 | EndGlobal 80 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/CC.AutomatedTesting.Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {980F233F-33BA-4683-B561-AC88A02D68BD} 8 | Exe 9 | Properties 10 | CC.AutomatedTesting.Demo 11 | CC.AutomatedTesting.Demo 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | true 38 | bin\x64\Debug\ 39 | DEBUG;TRACE 40 | full 41 | x64 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | true 45 | 46 | 47 | bin\x64\Release\ 48 | TRACE 49 | true 50 | pdbonly 51 | x64 52 | prompt 53 | MinimumRecommendedRules.ruleset 54 | true 55 | 56 | 57 | 58 | ..\packages\Ccr.Core.1.5.507.0\lib\net35\Ccr.Core.dll 59 | True 60 | 61 | 62 | ..\packages\log4net.1.2.10.1\lib\net11\log4net.dll 63 | True 64 | 65 | 66 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 67 | True 68 | 69 | 70 | ..\packages\RabbitMQ.Client.2.8.7.0\lib\net35\RabbitMQ.Client.dll 71 | True 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ..\packages\Selenium.WebDriver.3.6.0\lib\net45\WebDriver.dll 85 | True 86 | 87 | 88 | ..\packages\Selenium.Support.3.5.2\lib\net40\WebDriver.Support.dll 89 | True 90 | 91 | 92 | ..\packages\Wintellect.PowerThreading.1.0.0.0\lib\net35\Wintellect.PowerThreading.dll 93 | True 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Designer 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | {3e3537e1-431c-4ec7-a092-631b8c1bcb23} 117 | CC.AutomatedTesting.Extends.Cloud 118 | 119 | 120 | {81fbdf32-88ab-4fb5-8af5-6a6d7c045214} 121 | CC.AutomatedTesting.Infrastructure 122 | 123 | 124 | {379efc05-3c3b-4c2b-aed0-2de5c38ddf88} 125 | CC.AutomatedTesting.Reporting 126 | 127 | 128 | 129 | 136 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/ProcessControl.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Demo 10 | { 11 | class ProcessControl 12 | { 13 | const string WorkSpace = "../../脚本/"; 14 | //控制执行流程 15 | //1. 读取指定目录下的所有子目录 16 | //2. 屏幕打印所有子目录 17 | //3. 等待输入子目录编号 18 | //4. 执行子目录下的所有文件 19 | private static void ExecuteFile(string filePath) 20 | { 21 | var lines = File.ReadAllLines(filePath, Encoding.Default); 22 | var suite = TestSuiteFactory.Instance.CreateTestSuiteFrom(lines, filePath, null); 23 | TestSuiteRunner runner = new TestSuiteRunner(); 24 | runner.Run(suite); 25 | } 26 | public static void ExecuteAllFilesInDir(string dirPath) 27 | { 28 | var files = Directory.GetFiles(dirPath).OrderBy(p=>p); 29 | var dirName = Path.GetFileName(dirPath); 30 | foreach (var file in files) 31 | { 32 | var fileName = Path.GetFileName(file); 33 | Console.WriteLine(""); 34 | Console.WriteLine("文件:" + Path.GetFileName(file)); 35 | ExecuteFile(file); 36 | Console.WriteLine("结束"); 37 | Console.WriteLine(""); 38 | } 39 | Console.WriteLine(""); 40 | Console.WriteLine("--Finished--"); 41 | } 42 | public static string GetDirToExecute() 43 | { 44 | var subDirs = Directory.GetDirectories(WorkSpace); 45 | int i = 1; 46 | var dict = subDirs.ToDictionary(kv => (i++).ToString(), kv => kv); 47 | 48 | var fileNameList = dict.Select(p => p.Key + "\t" + Path.GetFileName(p.Value)).ToList(); 49 | fileNameList.ForEach(p => { Console.WriteLine(p); }); 50 | 51 | while (true) 52 | { 53 | Console.Write("请输入要执行的目录序号:"); 54 | var num = Console.ReadLine(); 55 | if (dict.ContainsKey(num)) 56 | { 57 | return dict[num]; 58 | } 59 | } 60 | } 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/Program.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure; 2 | using CC.AutomatedTesting.Infrastructure.Exceptions; 3 | using CC.AutomatedTesting.Infrastructure.FunctionProcessor; 4 | using CC.AutomatedTesting.Infrastructure.ParallelRun; 5 | using CC.AutomatedTesting.Infrastructure.TextAnalyzer; 6 | using CC.AutomatedTesting.Reporting; 7 | using OpenQA.Selenium; 8 | using OpenQA.Selenium.Chrome; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Linq; 13 | using System.Text; 14 | using System.Threading; 15 | using System.Threading.Tasks.Dataflow; 16 | 17 | namespace CC.AutomatedTesting.Demo 18 | { 19 | class Program 20 | { 21 | static void Main(string[] args) 22 | { 23 | //var funcDefinitions = LoadFunctionsFromFiles(); 24 | var dirPath = ProcessControl.GetDirToExecute(); 25 | ProcessControl.ExecuteAllFilesInDir(dirPath); 26 | } 27 | //读函数 28 | private static Dictionary LoadFunctionsFromFiles() 29 | { 30 | var funFiles = GetFunctionDefineFileNames(); 31 | var funScripts = funFiles.SelectMany(p => LoadFromFile(p)).ToList(); 32 | var preprocessResult = TextPreProcesser.PreprocessV2(funScripts.ToList()); 33 | var funs = preprocessResult.Item3; 34 | var funcDefinitions = FuncProcessor.Instance.GetFunctionDefines(funs); 35 | return funcDefinitions; 36 | } 37 | 38 | private static TestSuite LoadTestSuiteFromFile(Dictionary funcDefinitions) 39 | { 40 | while(true) 41 | { 42 | try 43 | { 44 | var file = GetFileName(); 45 | var scriptContent = LoadFromFile(file); 46 | var suite = TestSuiteFactory.Instance.CreateTestSuiteFrom(scriptContent, file, funcDefinitions); 47 | return suite; 48 | } 49 | catch (BizException ex) 50 | { 51 | Console.WriteLine("脚本错误:"+ex.ToUserInfo()); 52 | Console.WriteLine(""); 53 | } 54 | catch(Exception ex) 55 | { 56 | Console.WriteLine("脚本异常:"+ex.Message+"\r\n"+ex.StackTrace); 57 | Console.WriteLine(""); 58 | } 59 | } 60 | } 61 | 62 | const string WorkSpace = "../../脚本/"; 63 | const string FunctionDirectory = WorkSpace + "函数/"; 64 | static string[] GetFunctionDefineFileNames() 65 | { 66 | return Directory.GetFiles(FunctionDirectory); 67 | } 68 | 69 | static string GetFileName() 70 | { 71 | Console.WriteLine("请输入要执行脚本的序号,并按Enter:"); 72 | var files = Directory.GetFiles(WorkSpace); 73 | Dictionary fileDict = new Dictionary(); 74 | var index = 1; 75 | foreach (var file in files) 76 | { 77 | fileDict.Add(index.ToString(), file); 78 | Console.WriteLine($"{index}\t{file}"); 79 | index++; 80 | } 81 | while(true) 82 | { 83 | var key = Console.ReadLine(); 84 | if (fileDict.Keys.Contains(key)) 85 | { 86 | return fileDict[key]; 87 | } 88 | } 89 | } 90 | 91 | 92 | public static string[] LoadFromFile(string filePath) 93 | { 94 | if (!File.Exists(filePath)) 95 | throw new FileNotFoundException(filePath + " Not Exist"); 96 | return File.ReadAllLines(filePath, Encoding.Default); 97 | } 98 | 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("CC.AutomatedTesting.Demo")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("CC.AutomatedTesting.Demo")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("980f233f-33ba-4683-b561-ac88a02d68bd")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/ReadME.txt: -------------------------------------------------------------------------------- 1 | 1. 根据标签,生成测试说明文档 2 | 2. 异常捕获处理 3 | 3. 截图 4 | 4. 生成报表 5 | 6 | 7 | 测试脚本中, 8 | 注释: #开头的行是注释,可以存在于脚本的任意位置,必须是一整行,#前面可以有空白字符。 9 | 测试用例开头的行,是一个测试用例的开始 10 | 验证语句:以“验证:”开头。中间不能有空格 11 | 12 | 命令头:参数1,参数2 13 | 命令头后面可以有任意空白,逗号分隔的每个参数前后也可以有任意空白 14 | 15 | 16 | 17 | 18 | 19 | 1.业务端在实现断言的时候,需要自己根据DOM元素,去对比是否匹配,同时是否截图。 20 | 2.出现异常时,系统会自动截图,并把截图放在相应的报表中 21 | 22 | 23 | 异常:没有找到命令对应的执行器。 如,QA把“打开窗口”写成了“打开页面”,导致找不到相应的Handler 24 | 25 | 26 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/bitbug_favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lucas20029/AutomatedTesting/b6205638f17c832853fed53a7a20acf6b8077031/CC.AutomatedTesting.Demo/bitbug_favicon.ico -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/使用时注意事项.txt: -------------------------------------------------------------------------------- 1 | 请尽量在大屏显示器上执行。 2 | 执行过程中,不要手动操作浏览器窗口。包括移动、最大化、最小化 -------------------------------------------------------------------------------- /CC.AutomatedTesting.Demo/脚本/流程1/贴吧登录.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lucas20029/AutomatedTesting/b6205638f17c832853fed53a7a20acf6b8077031/CC.AutomatedTesting.Demo/脚本/流程1/贴吧登录.txt -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/CC.AutomatedTesting.Extends.Cloud.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {3E3537E1-431C-4EC7-A092-631B8C1BCB23} 8 | Library 9 | Properties 10 | CC.AutomatedTesting.Extends.Cloud 11 | CC.AutomatedTesting.Extends.Cloud 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | true 36 | bin\x64\Debug\ 37 | DEBUG;TRACE 38 | full 39 | x64 40 | prompt 41 | MinimumRecommendedRules.ruleset 42 | 43 | 44 | bin\x64\Release\ 45 | TRACE 46 | true 47 | pdbonly 48 | x64 49 | prompt 50 | MinimumRecommendedRules.ruleset 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ..\packages\Selenium.WebDriver.3.6.0\lib\net45\WebDriver.dll 65 | True 66 | 67 | 68 | ..\packages\Selenium.Support.3.5.2\lib\net40\WebDriver.Support.dll 69 | True 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | {81fbdf32-88ab-4fb5-8af5-6a6d7c045214} 89 | CC.AutomatedTesting.Infrastructure 90 | 91 | 92 | 93 | 94 | 95 | 96 | 此项目引用这台计算机上缺少的 NuGet 程序包。使用 NuGet 程序包还原可下载这些程序包。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 97 | 98 | 99 | 100 | 107 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/LoginTiebaAction.cs: -------------------------------------------------------------------------------- 1 | using CC.Meeting.DataConvert.CommonConvert; 2 | using CC.AutomatedTesting.Infrastructure; 3 | using CC.AutomatedTesting.Infrastructure.Exceptions; 4 | using CC.AutomatedTesting.Infrastructure.Extensions; 5 | using OpenQA.Selenium; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Threading; 12 | 13 | namespace CC.AutomatedTesting.CCBiz 14 | { 15 | [ActionMethod("登录贴吧")] 16 | [VersionControl(Range = "1..")] 17 | public class LoginTiebaAction : PerformAction 18 | { 19 | public LoginTiebaAction() 20 | { 21 | } 22 | public override void Perform() 23 | { 24 | WebDriver.Navigate().GoToUrl("https://tieba.baidu.com/index.html"); 25 | 26 | /* 27 | //var userBar = WebDriver.SafeFindElement(By.Id("com_userbar")); 28 | //var loginBtn = userBar.SafeFindElement(By.CssSelector(".u_login a")); 29 | var loginBtn = WebDriver.SafeFindElement(By.CssSelector("#com_userbar .u_login .u_menu_item a")); 30 | 31 | loginBtn.Click(); 32 | var userPwdLogin = WebDriver.SafeFindElement(By.ClassName("tang-pass-footerBarULogin")); 33 | userPwdLogin.Click(); 34 | var userName = WebDriver.SafeFindElement(By.Id("TANGRAM__PSP_10__userName")); 35 | var pwd = WebDriver.SafeFindElement(By.Id("TANGRAM__PSP_10__password")); 36 | userName.SendKeys(Parameters[0]); 37 | pwd.SendKeys(Parameters[1]); 38 | var submitBtn = WebDriver.SafeFindElement(By.Id("TANGRAM__PSP_10__submit")); 39 | submitBtn.Click(); 40 | //var safeForceWin = WebDriver.SafeFindElement(By.ClassName("pass-forceverify-wrapper")); 41 | //if(safeForceWin!=null)//如果弹出来了【安全验证】的窗口,则直接关闭掉 42 | //{ 43 | // var closeBtn = safeForceWin.SafeFindElement(By.ClassName("forceverify-header-a")); 44 | // closeBtn.Click(); 45 | //} 46 | Waiter.UntilDisappear(By.ClassName("passport-login-pop"),WebDriver); 47 | */ 48 | 49 | } 50 | 51 | public override void PostConditionCheck() 52 | { 53 | } 54 | 55 | public override void PreConditionCheck() 56 | { 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("CC.AutomatedTesting.CCBiz")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("CC.AutomatedTesting.CCBiz")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("3e3537e1-431c-4ec7-a092-631b8c1bcb23")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/Random.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 CC.AutomatedTesting.Extends.CCCloud 8 | { 9 | public class RandomResult 10 | { 11 | public static int Next(int resultCount) 12 | { 13 | Random ran = new Random(); 14 | return ran.Next() % resultCount; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/SearchAction.cs: -------------------------------------------------------------------------------- 1 | using CC.Meeting.DataConvert.CommonConvert; 2 | using CC.AutomatedTesting.Infrastructure; 3 | using CC.AutomatedTesting.Infrastructure.Exceptions; 4 | using CC.AutomatedTesting.Infrastructure.Extensions; 5 | using OpenQA.Selenium; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Threading; 12 | 13 | namespace CC.AutomatedTesting.CCBiz 14 | { 15 | [ActionMethod("搜索")] 16 | [VersionControl(Range = "1..")] 17 | public class SearchAction : PerformAction 18 | { 19 | public SearchAction() 20 | { 21 | } 22 | public override void Perform() 23 | { 24 | var searchBox = WebDriver.SafeFindElement(By.CssSelector(".j_search_form .search_ipt")); 25 | searchBox.SendKeys(Parameters[0]); 26 | } 27 | 28 | public override void PostConditionCheck() 29 | { 30 | } 31 | 32 | public override void PreConditionCheck() 33 | { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/TableList/AssertTableExistLinkText.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure; 2 | using CC.AutomatedTesting.Infrastructure.Extensions; 3 | using OpenQA.Selenium; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CC.AutomatedTesting.CCBiz 11 | { 12 | [ActionMethod("表格中存在链接")] 13 | [VersionControl(Range = "0..2")] 14 | public class AssertTableExistLinkTextV1 : AssertAction 15 | { 16 | public AssertTableExistLinkTextV1() 17 | { 18 | this.ActionRequiredIframeIDPath = new List() { "iTalFrame" }; 19 | } 20 | public override AssertionResult Assert() 21 | { 22 | var cellText = Parameters[0]; 23 | try 24 | { 25 | Waiter.Until(p => p.FindElement(By.LinkText(cellText))); //等待该元素出现 26 | //在表格中查找 27 | var targetLink = WebDriver.SafeFindElement(By.ClassName("dataGrid"))?.FindElement(By.LinkText(cellText)); 28 | if (targetLink == null) 29 | throw new Exception(); 30 | return AssertionResult.Success("验证通过,存在" + cellText); 31 | 32 | } 33 | catch (Exception ex) 34 | { 35 | var screenShotAddress = WebDriver.ScreenShot(); 36 | return AssertionResult.Fail("未找到该元素", "表格中存在链接" + cellText, screenShotAddress); 37 | } 38 | 39 | } 40 | 41 | public override void PostConditionCheck() 42 | { 43 | //throw new NotImplementedException(); 44 | } 45 | 46 | public override void PreConditionCheck() 47 | { 48 | //throw new NotImplementedException(); 49 | } 50 | } 51 | 52 | [ActionMethod("表格中存在链接")] 53 | [VersionControl(Range = "2..")] 54 | public class AssertTableExistLinkTextV2 : AssertAction 55 | { 56 | public AssertTableExistLinkTextV2() 57 | { 58 | this.ActionRequiredIframeIDPath = new List() { "iTalFrame" }; 59 | } 60 | public override AssertionResult Assert() 61 | { 62 | var cellText = Parameters[0]; 63 | try 64 | { 65 | Waiter.Until(p => p.FindElement(By.LinkText(cellText))); //等待该元素出现 66 | //在表格中查找 67 | var targetLink = WebDriver.FindElement(By.ClassName("el-table__body"))?.FindElement(By.LinkText(cellText)); 68 | if (targetLink == null) 69 | throw new Exception(); 70 | return AssertionResult.Success("验证通过,存在"+cellText); 71 | 72 | } 73 | catch (Exception ex) 74 | { 75 | var screenShotAddress = WebDriver.ScreenShot(); 76 | return AssertionResult.Fail("未找到该元素", "表格中存在" + cellText, screenShotAddress); 77 | } 78 | 79 | } 80 | 81 | public override void PostConditionCheck() 82 | { 83 | //throw new NotImplementedException(); 84 | } 85 | 86 | public override void PreConditionCheck() 87 | { 88 | //throw new NotImplementedException(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/TestAssert1Action.cs: -------------------------------------------------------------------------------- 1 | using CC.Meeting.DataConvert.CommonConvert; 2 | using CC.AutomatedTesting.Infrastructure; 3 | using System; 4 | 5 | namespace CC.AutomatedTesting.CCBiz 6 | { 7 | [ActionMethod("测试断言1")] 8 | public class TestAssert2Action : AssertAction 9 | { 10 | 11 | public override AssertionResult Assert() 12 | { 13 | if (Parameters[0].To() == 0) //验证成功 14 | { 15 | return AssertionResult.Success("第一列列名是" + Parameters[1]); 16 | } 17 | else if (Parameters[0].To() == 1) //验证未通过 18 | { 19 | return AssertionResult.Fail($"第一列是XXOO", $"第一列列名应该是{Parameters[1]}"); 20 | } 21 | else //执行过程中,抛出异常 22 | { 23 | throw new Exception("测试断言2出现异常"); 24 | } 25 | } 26 | 27 | public override void PostConditionCheck() 28 | { 29 | } 30 | 31 | public override void PreConditionCheck() 32 | { 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/TestAssert2Action.cs: -------------------------------------------------------------------------------- 1 | using CC.Meeting.DataConvert.CommonConvert; 2 | using CC.AutomatedTesting.Infrastructure; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.CCBiz 10 | { 11 | [ActionMethod("测试断言2")] 12 | public class TestAssert1Action : AssertAction 13 | { 14 | 15 | public override AssertionResult Assert() 16 | { 17 | if (Parameters[0].To() == 0) //验证成功 18 | { 19 | return AssertionResult.Success("第一列列名是" + Parameters[1]); 20 | } 21 | else if (Parameters[0].To() == 1) //验证未通过 22 | { 23 | return AssertionResult.Fail($"第一列是XXOO", $"第一列列名应该是{Parameters[1]}"); 24 | } 25 | else //执行过程中,抛出异常 26 | { 27 | throw new Exception("测试断言2出现异常"); 28 | } 29 | } 30 | 31 | public override void PostConditionCheck() 32 | { 33 | } 34 | 35 | public override void PreConditionCheck() 36 | { 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/ThreadSleepAction.cs: -------------------------------------------------------------------------------- 1 | using CC.Meeting.DataConvert.CommonConvert; 2 | using CC.AutomatedTesting.Infrastructure; 3 | using CC.AutomatedTesting.Infrastructure.Exceptions; 4 | using OpenQA.Selenium; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace CC.AutomatedTesting.CCBiz 13 | { 14 | [ActionMethod("暂停")] 15 | public class ThreadSleepAction : PerformAction 16 | { 17 | public override void Perform() 18 | { 19 | var milSecondStr = Parameters[0]; 20 | int milSecondToWait = 1000; 21 | if(string.IsNullOrEmpty(milSecondStr) || int.TryParse(milSecondStr,out milSecondToWait)) 22 | { 23 | Thread.Sleep(milSecondToWait); 24 | } 25 | } 26 | 27 | public override void PostConditionCheck() 28 | { 29 | } 30 | 31 | public override void PreConditionCheck() 32 | { 33 | 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Extends.Cloud/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure.Tests/CC.AutomatedTesting.Infrastructure.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {114A7D59-793C-4AF0-AA17-0424C13DA9C2} 7 | Library 8 | Properties 9 | CC.AutomatedTesting.Infrastructure.Tests 10 | CC.AutomatedTesting.Infrastructure.Tests 11 | v4.5 12 | 512 13 | 10.0 14 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 15 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 16 | False 17 | UnitTest 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | true 38 | bin\x64\Debug\ 39 | DEBUG;TRACE 40 | full 41 | x64 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | 45 | 46 | bin\x64\Release\ 47 | TRACE 48 | true 49 | pdbonly 50 | x64 51 | prompt 52 | MinimumRecommendedRules.ruleset 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | {81fbdf32-88ab-4fb5-8af5-6a6d7c045214} 84 | CC.AutomatedTesting.Infrastructure 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | False 101 | 102 | 103 | False 104 | 105 | 106 | False 107 | 108 | 109 | False 110 | 111 | 112 | 113 | 114 | 115 | 116 | 123 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("CC.AutomatedTesting.Infrastructure.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("CC.AutomatedTesting.Infrastructure.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("114a7d59-793c-4af0-aa17-0424c13da9c2")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure.Tests/TextAnalyzer/AnalyzerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using CC.AutomatedTesting.Infrastructure.TextAnalyzer; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Text.RegularExpressions; 9 | 10 | namespace CC.AutomatedTesting.Infrastructure.TextAnalyzer.Tests 11 | { 12 | [TestClass()] 13 | public class AnalyzerTests 14 | { 15 | 16 | [TestMethod()] 17 | public void Test_RegexVariableName() 18 | { 19 | string regex = @":\s*[^=\s]+\s*="; 20 | string source = "定义 :变量4=1000=[@时间戳"; 21 | var result = Regex.Match(source, regex).Value; 22 | Assert.IsTrue(result== ":变量4="); 23 | } 24 | 25 | [TestMethod()] 26 | public void Test_RegexVariableDefines() 27 | { 28 | string regex = @"^\s*定义\s*:\s*[^=\s]+\s*=\s*\S+\s*$"; 29 | string source = " 定义 :变量2 = 1000时间戳 "; 30 | var result = Regex.IsMatch(source,regex); 31 | Assert.IsTrue(result); 32 | } 33 | 34 | [TestMethod()] 35 | public void Test_EatVariableDefines() 36 | { 37 | List text = new List() 38 | { 39 | "//123123", 40 | "", 41 | "定义 : 变量1 = 变量2 ", 42 | " 定义:变量2=1000@时间戳", 43 | "//", 44 | " ", 45 | "测试123123", 46 | "123123" 47 | }; 48 | var expected = new List() 49 | { 50 | "//123123", 51 | "", 52 | "//", 53 | " ", 54 | "测试123123", 55 | "123123" 56 | }; 57 | var expectedVariable = new List() 58 | { 59 | "定义 : 变量1 = 变量2 ", 60 | " 定义:变量2=1000@时间戳", 61 | }; 62 | List definedLines = new List(); 63 | var result = TextPreProcesser.EatVariableDefines(text, out definedLines); 64 | Assert.IsTrue(IsListEquals(result, expected)); 65 | Assert.IsTrue(IsListEquals(definedLines, expectedVariable)); 66 | } 67 | [TestMethod()] 68 | public void Test_Explain() 69 | { 70 | var defineVariable = new List() 71 | { 72 | "定义 : 变量1 = 变量2 ", 73 | " 定义:变量2=1000@时间戳", 74 | " 定义:变量3=随机数@随机数109", 75 | " 定义 :变量4=1000=[@时间戳", 76 | }; 77 | var resultDict = TextPreProcesser.Explain(defineVariable); 78 | Assert.IsTrue(resultDict.Count == 4); 79 | Assert.IsTrue(resultDict["变量1"]=="变量2"); 80 | Assert.IsTrue(resultDict["变量2"].StartsWith("1000")); 81 | Assert.IsTrue(resultDict["变量3"].StartsWith("随机数")); 82 | Assert.IsTrue(resultDict["变量3"].EndsWith("109")); 83 | 84 | } 85 | 86 | 87 | private bool IsListEquals(List source, List target) 88 | { 89 | if (source.Count != target.Count) 90 | return false; 91 | for(int i = 0; i < source.Count; i++) 92 | { 93 | if (source[i] != target[i]) 94 | return false; 95 | } 96 | return true; 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure.Tests/UserFunction/FunctionTest.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.FunctionProcessor; 2 | using CC.AutomatedTesting.Infrastructure.TextAnalyzer; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace CC.AutomatedTesting.Infrastructure.Tests.UserFunction 12 | { 13 | [TestClass()] 14 | public class FunctionTest 15 | { 16 | [TestMethod()] 17 | public void Test_ConvertFunc() 18 | { 19 | try 20 | { 21 | List actualParam = new List 22 | { 23 | "测试发薪方案","薪酬1","月" 24 | }; 25 | var scriptContent = LoadFromFile("../../脚本/函数1.txt"); 26 | var tupleObj = TextPreProcesser.PreprocessV2(scriptContent.ToList()); 27 | 28 | FuncProcessor.Instance.Process(actualParam, tupleObj.Item3); 29 | } 30 | catch (Exception ex) 31 | { 32 | 33 | throw; 34 | } 35 | 36 | } 37 | public static string[] LoadFromFile(string filePath) 38 | { 39 | if (!File.Exists(filePath)) 40 | throw new FileNotFoundException(filePath + " Not Exist"); 41 | return File.ReadAllLines(filePath, Encoding.Default); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure.Tests/VersionControl/VersionHelperTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using CC.AutomatedTesting.Infrastructure; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.Tests 10 | { 11 | [TestClass()] 12 | public class VersionHelperTests 13 | { 14 | [TestMethod()] 15 | public void Test_AnalyzeVersionCode() 16 | { 17 | var result = Version.FromString("1.16.9-01-alpha"); 18 | Assert.IsTrue(result.Codes.Count() == 5); 19 | Assert.IsTrue(result.Codes[0] == 1); 20 | Assert.IsTrue(result.Codes[1] == 16); 21 | Assert.IsTrue(result.Codes[2] == 9); 22 | Assert.IsTrue(result.Codes[3] == 1); 23 | Assert.IsTrue(result.Codes[4] == 1); 24 | } 25 | 26 | [TestMethod()] 27 | public void Test_AnalyzeVersion() 28 | { 29 | var result = VersionSeries.FromString("1.16.9-01-alpha..1.16.9-20,1.16.11"); 30 | Assert.IsTrue(result.Ranges.Count() == 1); 31 | Assert.IsTrue(result.Ranges[0].Item2.Codes[3] == 20); 32 | Assert.IsTrue(result.Singles[0].Codes[2] == 11); 33 | } 34 | [TestMethod()] 35 | public void Test_AnalyzeVersion_Empty() 36 | { 37 | var result = VersionSeries.FromString("*"); 38 | Assert.IsTrue(result.Ranges[0].Item1.Codes[0] == int.MinValue); 39 | Assert.IsTrue(result.Ranges[0].Item2.Codes[0] == int.MaxValue); 40 | } 41 | 42 | [TestMethod()] 43 | public void Test_CompareTo_Similar() 44 | { 45 | Version v1 = Version.FromString("1.16.9-01"); 46 | Version v2 = Version.FromString("1.16.9-05"); 47 | Assert.IsTrue(v1 <= v2); 48 | } 49 | 50 | [TestMethod()] 51 | public void Test_CompareTo_ShortVersion() 52 | { 53 | Version v1 = Version.FromString("2"); 54 | Version v2 = Version.FromString("1.16.9-05"); 55 | Assert.IsTrue(v1 >= v2); 56 | } 57 | 58 | [TestMethod()] 59 | public void Test_CompareTo_WithBeta() 60 | { 61 | Version v1 = Version.FromString("1.16.9-01"); 62 | Version v2 = Version.FromString("1.16.9-01-beta"); 63 | Assert.IsTrue(v1 <= v2); 64 | } 65 | 66 | [TestMethod()] 67 | public void Test_IsBelongsToMe() 68 | { 69 | var versions = VersionSeries.FromString("0..1.16.9,1.19.1,2.8.5-alpha"); 70 | var version = Version.FromString("1.12.0"); 71 | var version1 = Version.FromString("2.8.5"); 72 | Assert.IsTrue(versions.IsBelongsToMe(version)); 73 | Assert.IsFalse(versions.IsBelongsToMe(version1)); 74 | } 75 | 76 | [TestMethod()] 77 | public void Test_IsAny() 78 | { 79 | var result = VersionSeries.AnySeries.IsAny(); 80 | Assert.IsTrue(result); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure.Tests/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure.Tests/脚本/函数1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lucas20029/AutomatedTesting/b6205638f17c832853fed53a7a20acf6b8077031/CC.AutomatedTesting.Infrastructure.Tests/脚本/函数1.txt -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/ActionFactory/AssertActionFactory.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using CC.AutomatedTesting.Infrastructure.Extensions; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Text.RegularExpressions; 9 | using System.Threading.Tasks; 10 | 11 | namespace CC.AutomatedTesting.Infrastructure.ActionFactory 12 | { 13 | public class AssertActionFactory : IActionFactory 14 | { 15 | #region Signleton 16 | private static readonly AssertActionFactory instance = new AssertActionFactory(); 17 | private AssertActionFactory() 18 | { 19 | } 20 | public static AssertActionFactory Instance 21 | { 22 | get 23 | { 24 | return instance; 25 | } 26 | } 27 | #endregion 28 | public AssertAction CreateAction(string userCommand, Version version, ConstructingContext context) 29 | { 30 | if (version == null) 31 | throw new UnSpecifiedVersionException(); 32 | 33 | if (!userCommand.StartsWith(Constants.Configs.AssertPrefix)) 34 | { 35 | throw new Exception("invalid user assert command. Should be started with " + Constants.Configs.AssertPrefix); 36 | } 37 | var firstSpliterIndex = userCommand.IndexOf(Constants.CommandParameterSpliter); 38 | if (firstSpliterIndex < 0) 39 | throw new InvalidCommandFormatException(userCommand); 40 | userCommand = userCommand.Remove(0, firstSpliterIndex+1).Trim();//删掉前缀(验证)和冒号 41 | 42 | var spliterIndex = userCommand.IndexOf(Constants.CommandParameterSpliter); 43 | if (spliterIndex < 0) 44 | throw new InvalidCommandFormatException(userCommand); 45 | var title = userCommand.Substring(0, spliterIndex).Trim(); // 获取命令前缀 “表格列存在” 46 | var parameterText = userCommand.Substring(spliterIndex+1, userCommand.Length - spliterIndex -1).Trim(); // 参数 47 | 48 | var action = ActionResolver.ResolveAssert(title, version); 49 | //var action = TypeContainer.Resolve(title, version); //如果实例化有问题,内部会抛出异常。不会返回null 50 | action.Parameters = parameterText.SplitAndTrim(Constants.ParameterSpliter).ToList().Translate(context.Bindings).Escape(); 51 | action.ActionName = title; 52 | action.CommandText = userCommand; 53 | return action; 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/ActionFactory/FuncActionFactory.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Bizbases; 2 | using CC.AutomatedTesting.Infrastructure.Extensions; 3 | using CC.AutomatedTesting.Infrastructure.FunctionProcessor; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CC.AutomatedTesting.Infrastructure.ActionFactory 11 | { 12 | public class FuncActionFactory : IActionFactory 13 | { 14 | #region Signleton 15 | private static readonly FuncActionFactory instance = new FuncActionFactory(); 16 | private FuncActionFactory() 17 | { 18 | } 19 | public static FuncActionFactory Instance 20 | { 21 | get 22 | { 23 | return instance; 24 | } 25 | } 26 | #endregion 27 | public FunctionAction CreateAction(string userCommand, Version version, ConstructingContext context) 28 | { 29 | FunctionAction funcAction = new FunctionAction(); 30 | var spliterIndex = userCommand.IndexOf(Constants.CommandParameterSpliter); 31 | var parameterText = userCommand.Substring(spliterIndex + 1, userCommand.Length - spliterIndex - 1).Trim(); 32 | string funcName = parameterText.Substring(0, parameterText.IndexOf("(")); 33 | List actualParamList = parameterText.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)?.ToList(); 34 | 35 | var orderSetDic = FuncProcessor.Instance.Process(actualParamList, context.FunctionText); 36 | List paramList = orderSetDic.ContainsKey(funcName) ? orderSetDic[funcName] : new List(); 37 | var instance = PerformActionFactory.Instance; 38 | funcAction.ActionName = funcName; 39 | funcAction.ActionName = parameterText; 40 | funcAction.CommandText = parameterText; 41 | foreach (var param in paramList) 42 | { 43 | funcAction.BodyActions.Add(instance.CreateAction(param.Trim(), version, context)); 44 | } 45 | 46 | return funcAction; 47 | 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/ActionFactory/IActionFactory.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 CC.AutomatedTesting.Infrastructure.ActionFactory 8 | { 9 | public interface IActionFactory where TAction :Action 10 | { 11 | TAction CreateAction(string userCommand, Version version, ConstructingContext context); 12 | } 13 | 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/ActionFactory/JsOrHtmlActionContainer.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Bizbases; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CC.AutomatedTesting.Infrastructure.ActionFactory 9 | { 10 | /* 11 | 测试命令的来源可以分为多种渠道: 12 | 1. .NET程序集 13 | 2. JS脚本执行器 14 | 3. HTML元素编排器 15 | 这三种渠道均可以输出测试命令 16 | 17 | 测试命令的分类: 18 | 1. 平台测试命令:以“命令名”的格式 19 | 2. 产品专属测试命令:以“产品名_命令名”格式 20 | 21 | 提供一个类,输入命令的名字、当前平台前端版本号,这个类可以输出一个固定的Object 22 | 示例: 23 | 输入:登录系统 输出: CC.Testing.Cloud.LoginAction的实例 24 | 输入:薪酬-修改表格 输出: CC.Testing.Compensation.ModifyTableListCellAction的实例 25 | 输入:填充文本框(如果是JS脚本命令) 输出: CC.Testing.Common.JSExecutorAction 的实例,把JS脚本属性给赋上值 26 | 输入:选择下拉菜单(如果是HTML编排) 输出: CC.Testing.Common.HtmlMakeUp的实例,把HtmlScript属性给赋上值 27 | 输出逻辑为: 28 | 查找顺序为: CSharpDll --> HTML编排器 --> JS脚本执行器 29 | 如果有符合条件的,就地结束,输出。如果没有符合条件的,继续向下查找 30 | */ 31 | /* 32 | 在编写测试后台时,在创建产品线测试命令的时候,需要在保存时,自动给加上产品线的名称,以后不管是呈现、使用,都以产品线_命令名为标准名称 33 | */ 34 | public class ActionResolver 35 | { 36 | public static PerformAction ResolvePerform(string actionName, Version requiredVersion) 37 | { 38 | return TypeContainer.Resolve(actionName, requiredVersion); 39 | //1. 先调用DllTypeContainer,看看是否能实例化对应的Action 40 | //2. 如果为空,则调用JSOrHTMLActionContainer,看看是否能实例化对应的Action 41 | //3.如果都不能,则抛出异常 42 | } 43 | public static AssertAction ResolveAssert(string actionName, Version requiredVersion) 44 | { 45 | return TypeContainer.Resolve(actionName, requiredVersion); 46 | } 47 | public static Action ResolveFunc(string actionName, Version requiredVersion) 48 | { 49 | return TypeContainer.Resolve(actionName, requiredVersion); 50 | } 51 | public static List GetAllActions() 52 | { 53 | var dllActions = TypeContainer.Lookups; 54 | return dllActions.Select(p => p.Key).ToList(); 55 | } 56 | } 57 | 58 | public class JsOrHtmlActionContainer 59 | { 60 | public PerformAction ResolvePerform(string actionName, Version requiredVersion) 61 | { 62 | //1. 查询多租赁指定的actionName,是否有名字匹配的结果 63 | //2. 如果有,则查看版本是否匹配 64 | //3. 如果存在版本匹配的,就看是JS类型的,就输出JSPerformAction 65 | // 是HTML类型的,就输出HTMLPerformAction 66 | //没有匹配的,就输出Null 67 | return null; 68 | } 69 | public PerformAction ResolveAssert(string actionName, Version requiredVersion) 70 | { 71 | //1. 查询多租赁指定的actionName,是否有名字匹配的结果 72 | //2. 如果有,则查看版本是否匹配 73 | //3. 如果存在版本匹配的,就看是JS类型的,就输出JSPerformAction 74 | // 是HTML类型的,就输出HTMLPerformAction 75 | //没有匹配的,就输出Null 76 | return null; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/ActionFactory/PerformActionFactory.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using CC.AutomatedTesting.Infrastructure.Extensions; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CC.AutomatedTesting.Infrastructure.ActionFactory 11 | { 12 | public class PerformActionFactory : IActionFactory 13 | { 14 | #region Signleton 15 | private static readonly PerformActionFactory instance = new PerformActionFactory(); 16 | private PerformActionFactory() 17 | { 18 | } 19 | public static PerformActionFactory Instance 20 | { 21 | get 22 | { 23 | return instance; 24 | } 25 | } 26 | #endregion 27 | public PerformAction CreateAction(string userCommand, Version version, ConstructingContext context) 28 | { 29 | if (version == null) 30 | throw new UnSpecifiedVersionException(); 31 | //命令不带参数的情况 32 | if (!userCommand.Contains(Constants.CommandParameterSpliter)) 33 | { 34 | var title = userCommand.Trim(); 35 | var action = ActionResolver.ResolvePerform(title, version); 36 | //var action = TypeContainer.Resolve(title,version); 37 | action.CommandText = userCommand; 38 | action.ActionName = title; 39 | action.Parameters = new List(); 40 | return action; 41 | } 42 | else 43 | { 44 | var spliterIndex = userCommand.IndexOf(Constants.CommandParameterSpliter); 45 | var title = userCommand.Substring(0, spliterIndex).Trim(); 46 | var parameterText = userCommand.Substring(spliterIndex + 1, userCommand.Length - spliterIndex - 1).Trim(); 47 | var action = ActionResolver.ResolvePerform(title, version); 48 | //var action = TypeContainer.Resolve(title,version); 49 | action.CommandText = userCommand; 50 | action.ActionName = title; 51 | action.Parameters = parameterText.SplitAndTrim(Constants.ParameterSpliter).ToList().Translate(context.Bindings).Escape(); 52 | return action; 53 | } 54 | 55 | 56 | //使用这种方法是不行的。 57 | //var actionFullName = TypeContainer.Mapping[title].FullName; 58 | //var action = Assembly.GetExecutingAssembly().CreateInstance(actionFullName) as PerformAction; 59 | 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/ActionFactory/TypeContainer.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CC.AutomatedTesting.Infrastructure.ActionFactory 11 | { 12 | /// 13 | /// 反射加载程序集、标签和类型 14 | /// 15 | public class TypeContainer 16 | { 17 | /// 18 | /// string:Action的命令,VersionSeries:支持的版本范围,Type:具体的类型 19 | /// 20 | private static Dictionary>> lookups; 21 | public static Dictionary>> Lookups 22 | { 23 | get 24 | { 25 | if (lookups == null || lookups.Count == 0) 26 | { 27 | LoadBizTestingDlls(); 28 | LoadLookup(); 29 | } 30 | return lookups; 31 | } 32 | } 33 | private static Dictionary mapping; 34 | public static Dictionary Mapping 35 | { 36 | get 37 | { 38 | if (mapping == null || mapping.Count == 0) 39 | { 40 | LoadBizTestingDlls(); 41 | LoadMappings(); 42 | } 43 | return mapping; 44 | } 45 | } 46 | public static T Resolve(string command) where T : class 47 | { 48 | if (!Mapping.ContainsKey(command)) 49 | { 50 | throw new CommandHandlerNotFound(command); 51 | } 52 | var result = Activator.CreateInstance(Mapping[command]) as T; 53 | if (result == null) 54 | { 55 | throw new CommandHandlerNotFound(command); 56 | } 57 | return result; 58 | } 59 | public static T Resolve(string command, Version version) where T : class 60 | { 61 | var type = GetTypeByCommandVersion(command, version); 62 | if (type == null) 63 | { 64 | throw new CommandHandlerNotFound(command); 65 | } 66 | var result = Activator.CreateInstance(type) as T; 67 | if (result == null) 68 | { 69 | throw new CommandHandlerNotFound(command); 70 | } 71 | return result; 72 | } 73 | /// 74 | /// 从指定目录把程序集加载到内存 75 | /// 76 | public static void LoadBizTestingDlls() 77 | { 78 | var applicationDirectory = Environment.CurrentDirectory; 79 | //var dllDir = applicationDirectory + "\\bizdlls"; 80 | //if (!Directory.Exists(dllDir)) 81 | //{ 82 | // Directory.CreateDirectory(dllDir); 83 | //} 84 | var dllNames = Directory.GetFiles(applicationDirectory).Where(p => p.EndsWith(".dll") && p.Contains("CC.AutomatedTesting")); 85 | foreach (var dllName in dllNames) 86 | { 87 | Assembly.LoadFrom(dllName); 88 | } 89 | } 90 | /// 91 | /// 从加载的程序集中,获取测试步骤类,放在mapping中 92 | /// 93 | private static void LoadMappings() 94 | { 95 | mapping = new Dictionary(); 96 | var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(p => p.FullName.Contains("CC.AutomatedTesting")); 97 | var publicTypes = assemblies.SelectMany(p => p.ExportedTypes); 98 | foreach (var type in publicTypes) 99 | { 100 | var actionAttribute = type.GetCustomAttributes(typeof(ActionMethodAttribute), false); 101 | if (actionAttribute != null && actionAttribute.Length > 0) 102 | { 103 | var title = (actionAttribute.First() as ActionMethodAttribute).Title; 104 | mapping.Add(title, type); 105 | } 106 | } 107 | } 108 | /// 109 | /// 从加载的程序集中,获取测试步骤类,放在mapping中 110 | /// 111 | private static void LoadLookup() 112 | { 113 | lookups = new Dictionary>>(); 114 | var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(p => p.FullName.Contains("CC.AutomatedTesting")); 115 | var publicTypes = assemblies.SelectMany(p => p.ExportedTypes); 116 | foreach (var type in publicTypes) 117 | { 118 | var actionAttribute = type.GetCustomAttributes(typeof(ActionMethodAttribute), false); 119 | if (actionAttribute != null && actionAttribute.Length > 0) 120 | { 121 | var title = (actionAttribute.First() as ActionMethodAttribute).Title; 122 | if (string.IsNullOrEmpty(title)) continue; 123 | if (!lookups.ContainsKey(title)) lookups.Add(title, new List>()); 124 | var versionAttribute = type.GetCustomAttribute(false); 125 | var versions = VersionSeries.AnySeries; 126 | if (versionAttribute != null && !string.IsNullOrEmpty(versionAttribute.Range)) 127 | { 128 | versions = VersionSeries.FromString(versionAttribute.Range); 129 | } 130 | lookups[title].Add(new Tuple(versions, type)); 131 | } 132 | } 133 | } 134 | private static Type GetTypeByCommandVersion(string command, Version version) 135 | { 136 | if (!Lookups.ContainsKey(command) || Lookups[command] == null) 137 | return null; 138 | Type tempDefaultType = null; 139 | Type formerType = null; 140 | foreach (var kv in Lookups[command]) 141 | { 142 | if (kv.Item1.IsAny()) 143 | { 144 | tempDefaultType = kv.Item2; 145 | continue;//假如脚本有2个按钮,优先匹配带版本的按钮,因此Continue; 146 | } 147 | if (kv.Item1.IsBelongsToMe(version)) 148 | { 149 | formerType = kv.Item2; 150 | break; 151 | } 152 | } 153 | if (formerType == null && tempDefaultType != null) 154 | { 155 | formerType = tempDefaultType; 156 | } 157 | return formerType; 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/Action.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using OpenQA.Selenium; 3 | using OpenQA.Selenium.Support.UI; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Text.RegularExpressions; 10 | using System.Threading.Tasks; 11 | 12 | namespace CC.AutomatedTesting.Infrastructure 13 | { 14 | public abstract class Action : IPreConditionCheck,IPostConditionCheck, INotifyPropertyChanged 15 | { 16 | #region Properties 17 | public string CommandText { get; set; } 18 | 19 | public string AnalyziedCommandText 20 | { 21 | get 22 | { 23 | return $"{ActionName}:{string.Join(",",Parameters)}"; 24 | } 25 | } 26 | 27 | //Action的中文名称 28 | public string ActionName { get; set; } 29 | //用于保存用户参数 30 | public List Parameters { get; internal set; } 31 | 32 | private ActionExecuteState _ExecuteState = ActionExecuteState.UnExecuted; 33 | public ActionExecuteState ExecuteState 34 | { 35 | get 36 | { 37 | return _ExecuteState; 38 | } 39 | set 40 | { 41 | _ExecuteState = value; 42 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ExecuteState"));//对Name进行监听 43 | } 44 | } 45 | 46 | public Exception InnerException { get; set; } 47 | public string Message { 48 | get 49 | { 50 | if (InnerException == null) 51 | return string.Empty; 52 | var ex = (InnerException as BizException); 53 | if (ex == null) 54 | return InnerException.Message; 55 | else 56 | return ex.ToUserInfo(); 57 | } 58 | } 59 | public bool IsFunction { get; set; } = false; 60 | #endregion 61 | 62 | /// 63 | /// Action运行前,要求的Iframe路径。支持正则表达式匹配IframeID路径 64 | /// 65 | protected List ActionRequiredIframeIDPath = new List() { }; 66 | /// 67 | /// 保存当前IframeID路径 68 | /// 69 | internal List CurrentIframeIDPath = new List() { }; 70 | 71 | private string CurrentIframeId { get; set; } 72 | 73 | const int WaitSeconds = 20; 74 | private IWebDriver webDriver; 75 | 76 | public event PropertyChangedEventHandler PropertyChanged; 77 | 78 | public IWebDriver WebDriver 79 | { 80 | get 81 | { 82 | return webDriver; 83 | } 84 | internal set 85 | { 86 | webDriver = value; 87 | Waiter = new WebDriverWait(webDriver, TimeSpan.FromSeconds(WaitSeconds)); 88 | } 89 | } 90 | public WebDriverWait Waiter { get; internal set; } 91 | 92 | /// 93 | /// 前置检查条件:本Action执行的前提是什么?。 94 | /// 注意:该方法在切换所需Iframe之后执行。 95 | /// 96 | public abstract void PreConditionCheck(); 97 | /// 98 | /// 后置检查条件:本Action完成的标准是什么? 99 | /// 100 | public abstract void PostConditionCheck(); 101 | public virtual void SwitchToActionRequiredIframe() 102 | { 103 | //如果所在iframe跟要求的不匹配,则从根向下逐级切换 104 | SwitchToIframe(ActionRequiredIframeIDPath); 105 | } 106 | 107 | #region Helper 108 | private bool IsInTheIframe(List iframeIdPath) 109 | { 110 | if(iframeIdPath.Count()==CurrentIframeIDPath.Count()) 111 | { 112 | for(int i=0;i< CurrentIframeIDPath.Count();i++) 113 | { //当前IframeID是否跟要求的IframeID匹配 114 | if (!Regex.IsMatch(CurrentIframeIDPath[i], iframeIdPath[i])) 115 | return false; 116 | } 117 | //全部匹配通过,则认为相等 118 | return true; 119 | } 120 | return false; 121 | } 122 | public virtual void SwitchToIframe(List iframeIdPath) 123 | { 124 | //如果所在iframe跟要求的不匹配,则从根向下逐级切换 125 | //if (!IsInTheIframe(iframeIdPath)) 126 | //{ 127 | CurrentIframeIDPath.Clear(); 128 | webDriver.SwitchTo().DefaultContent(); 129 | foreach (var frameId in iframeIdPath) 130 | { 131 | var targetFrame = webDriver 132 | .FindElements(By.TagName("iframe")) 133 | .LastOrDefault(p => Regex.IsMatch(p.GetAttribute("id"), frameId)); 134 | if (targetFrame == null) 135 | throw new IFrameNotFoundException(ActionName, frameId); 136 | var iframeId = targetFrame.GetAttribute("id").Clone().ToString(); 137 | webDriver.SwitchTo().Frame(targetFrame); 138 | CurrentIframeIDPath.Add(iframeId); 139 | } 140 | //} 141 | } 142 | 143 | public void Excuting() 144 | { 145 | ExecuteState = ActionExecuteState.Executing; 146 | } 147 | public void Success() 148 | { 149 | ExecuteState = ActionExecuteState.Succeed; 150 | } 151 | public void Fail(Exception ex=null) 152 | { 153 | ExecuteState = ActionExecuteState.Failed; 154 | InnerException = ex; 155 | } 156 | #endregion 157 | 158 | public enum ActionExecuteState 159 | { 160 | UnExecuted, 161 | Executing, 162 | Succeed, 163 | Failed, 164 | AssertionPass, 165 | AssertionFail 166 | } 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/ActionDescription.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 CC.AutomatedTesting.Infrastructure.Bizbases 8 | { 9 | public class ActionDescription 10 | { 11 | public string ActionName { get; set; } 12 | public string ActionNotation { get; set; } 13 | public List Parameters { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/ActionMethodAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace CC.AutomatedTesting.Infrastructure 9 | { 10 | public class ActionMethodAttribute : Attribute 11 | { 12 | //示例: 13 | //[ActionMethod("弹窗部门选择", "在部门选择框中的部门树上,按指定的层级的名称依次查找指定部门,并选择最终的部门","选择框控件标题。。部门路径,以-分隔")] 14 | public ActionMethodAttribute(string title,string description="", string paramDescStr="") 15 | { 16 | Title = title; 17 | Description = description; 18 | ParamDesc = new List(); 19 | if (!string.IsNullOrEmpty(paramDescStr.Trim())) 20 | { 21 | string[] paramArray = Regex.Split(paramDescStr, "。。", RegexOptions.IgnoreCase); 22 | if (paramArray != null) 23 | ParamDesc= paramArray.ToList(); 24 | } 25 | } 26 | public string Title { get; private set; } 27 | private string Description { get; set; } 28 | private List ParamDesc { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/AssertAction.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 CC.AutomatedTesting.Infrastructure 8 | { 9 | public abstract class AssertAction : Action, IAssert 10 | { 11 | //业务端实现的时候,需要根据DOM元素的比较结果,返回不同的AssertionResult Success 或 Fail(是否有截图,业务端可以自己决定) 12 | public virtual AssertionResult Assert() 13 | { 14 | return AssertionResult.Success("该断言未实现"); 15 | } 16 | } 17 | 18 | public class AssertionResult 19 | { 20 | public bool IsOk { get; set; } 21 | public string ActualResult { get; set; } 22 | public string ExpectedResult { get; set; } 23 | public string ScreenShot { get; set; } 24 | 25 | public static AssertionResult Success(string actualResult="") 26 | { 27 | return new AssertionResult() 28 | { 29 | IsOk = true, 30 | ActualResult = actualResult, 31 | ExpectedResult = actualResult 32 | }; 33 | } 34 | public static AssertionResult Fail(string actualResult, string expectedResult) 35 | { 36 | return new AssertionResult() 37 | { 38 | IsOk = false, 39 | ActualResult = actualResult, 40 | ExpectedResult = expectedResult 41 | }; 42 | } 43 | public static AssertionResult Fail(string actualResult, string expectedResult, string screenShot) 44 | { 45 | return new AssertionResult() 46 | { 47 | IsOk = false, 48 | ActualResult = actualResult, 49 | ExpectedResult = expectedResult, 50 | ScreenShot=screenShot 51 | }; 52 | } 53 | public override string ToString() 54 | { 55 | string resultInfo = IsOk ?"成功":"失败"; 56 | return $"验证{resultInfo}:期望结果:{ExpectedResult},实际结果:{ActualResult},屏幕截图:{ScreenShot}"; 57 | } 58 | 59 | public static AssertionResult Fail(object p, string screenShotAddress) 60 | { 61 | throw new NotImplementedException(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/CommonActions/HtmlAssertAction.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using OpenQA.Selenium; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.Bizbases.CommonActions 10 | { 11 | public class HtmlAssertAction : AssertAction 12 | { 13 | 14 | public string Version { get; private set; } 15 | public List HtmlScripts { get; private set; } 16 | public HtmlAssertAction(string actionName, List htmlScripts) 17 | { 18 | ActionName = actionName; 19 | HtmlScripts = htmlScripts; 20 | } 21 | public override AssertionResult Assert() 22 | { 23 | foreach (var script in HtmlScripts) 24 | { 25 | //TODO:执行Script。参考雪姣南京实现和RobotFramework 26 | } 27 | return base.Assert(); 28 | } 29 | 30 | public override void PreConditionCheck() 31 | { 32 | //throw new NotImplementedException(); 33 | } 34 | 35 | public override void PostConditionCheck() 36 | { 37 | //throw new NotImplementedException(); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/CommonActions/HtmlPerfromAction.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using OpenQA.Selenium; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.Bizbases.CommonActions 10 | { 11 | public enum ByType 12 | { 13 | ClassName, 14 | CssSelector, 15 | Id, 16 | LinkText, 17 | Name, 18 | PartialLinkText, 19 | TagName, 20 | XPath 21 | } 22 | public class HtmlScript 23 | { 24 | /// 25 | /// 根据 Id、Name、ClassName、CssSelector、XPath查找 26 | /// 27 | public ByType ByType { get; set; } 28 | public string ByContent { get; set; } 29 | 30 | } 31 | public class HtmlPerfromAction : PerformAction 32 | { 33 | 34 | public string Version { get; private set; } 35 | public List HtmlScripts { get; private set; } 36 | public HtmlPerfromAction(string actionName, List htmlScripts) 37 | { 38 | ActionName = actionName; 39 | HtmlScripts = htmlScripts; 40 | } 41 | public override void Perform() 42 | { 43 | try 44 | { 45 | foreach(var script in HtmlScripts) 46 | { 47 | //TODO:执行Script 48 | } 49 | } 50 | catch (Exception ex) 51 | { 52 | throw new JavascriptRuntimeException(ActionName, ex.Message); 53 | } 54 | } 55 | 56 | public override void PreConditionCheck() 57 | { 58 | //throw new NotImplementedException(); 59 | } 60 | 61 | public override void PostConditionCheck() 62 | { 63 | //throw new NotImplementedException(); 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/CommonActions/JsAssertAction.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using OpenQA.Selenium; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.Bizbases.CommonActions 10 | { 11 | public class JsAssertAction:AssertAction 12 | { 13 | public string Version { get; private set; } 14 | public string JsScript { get; private set; } 15 | public JsAssertAction(string actionName, string jsScript) 16 | { 17 | ActionName = actionName; 18 | JsScript = jsScript; 19 | } 20 | public override AssertionResult Assert() 21 | { 22 | try 23 | { 24 | var jsResult =((IJavaScriptExecutor)WebDriver).ExecuteScript(JsScript); 25 | return AssertionResult.Success(); 26 | } 27 | catch (Exception ex) 28 | { 29 | throw new JavascriptRuntimeException(ActionName,ex.Message); 30 | } 31 | } 32 | 33 | public override void PreConditionCheck() 34 | { 35 | //throw new NotImplementedException(); 36 | } 37 | 38 | public override void PostConditionCheck() 39 | { 40 | //throw new NotImplementedException(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/CommonActions/JsPerfromAction.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using OpenQA.Selenium; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.Bizbases.CommonActions 10 | { 11 | public class JsPerfromAction:PerformAction 12 | { 13 | public string Version { get; private set; } 14 | public string JsScript { get; private set; } 15 | public JsPerfromAction(string actionName,string jsScript) 16 | { 17 | ActionName = actionName; 18 | JsScript = jsScript; 19 | } 20 | public override void Perform() 21 | { 22 | try 23 | { 24 | ((IJavaScriptExecutor)WebDriver).ExecuteScript(JsScript); 25 | } 26 | catch (Exception ex) 27 | { 28 | throw new JavascriptRuntimeException(ActionName, ex.Message); 29 | } 30 | } 31 | 32 | public override void PreConditionCheck() 33 | { 34 | //throw new NotImplementedException(); 35 | } 36 | 37 | public override void PostConditionCheck() 38 | { 39 | //throw new NotImplementedException(); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/FuncAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CC.AutomatedTesting.Infrastructure.Bizbases 9 | { 10 | public class FuncAction : PerformAction 11 | { 12 | public ObservableCollection FucntionActions { get; set; } = new ObservableCollection(); 13 | public string FuncName { get; set; } 14 | public override void PostConditionCheck() 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | 19 | public override void PreConditionCheck() 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | public FuncAction() => IsFunction = true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/FunctionAction.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.FunctionProcessor; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure 10 | { 11 | public class FunctionAction :PerformAction 12 | { 13 | public ObservableCollection BodyActions { get; set; } 14 | public UserFunctionActual FunctionActual { get; set; } 15 | 16 | public override void PostConditionCheck() 17 | { 18 | 19 | } 20 | public override void PreConditionCheck() 21 | { 22 | 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/IAssert.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 CC.AutomatedTesting.Infrastructure 8 | { 9 | public interface IAssert 10 | { 11 | AssertionResult Assert(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/IPerform.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 CC.AutomatedTesting.Infrastructure 8 | { 9 | public interface IPerform 10 | { 11 | void Perform(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/IPreconditionCheck.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 CC.AutomatedTesting.Infrastructure 8 | { 9 | public interface IPreConditionCheck 10 | { 11 | void PreConditionCheck();//如果检查失败,则抛出异常 12 | } 13 | public interface IPostConditionCheck 14 | { 15 | void PostConditionCheck();//如果检查失败,则抛出异常 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/PerformAction.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 CC.AutomatedTesting.Infrastructure 8 | { 9 | public abstract class PerformAction : Action, IPerform 10 | { 11 | public virtual void Perform() 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Bizbases/VersionControlAttribute.cs: -------------------------------------------------------------------------------- 1 | using CC.Meeting.DataConvert.CommonConvert; 2 | using CC.AutomatedTesting.Infrastructure.Exceptions; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure 10 | { 11 | /* 12 | 版本比较规则:从左到右,逐位比较。例如: 2.8.6.1 >(晚于) 2.8.2.5 13 | 2.5.1.. 表示2.5.1到以后任意版本 14 | 0..2.5.1 表示0到2.5.1 15 | 1.2.5-9..1.4.6-08 表示1.2.5-9版本到1.4.6-08版本 16 | 1.2.5-01-alpha .. 2.3.9-5-beta 从 1.2.5-01-alpha 到 2.3.9-5-beta 17 | 1.2.5-01-alpha .. 2.3.9-5-beta ,1.16.11,2.1.20.. 表示1.2.5-01-alpha 到 2.3.9-5-beta,和1.16.11以及2.1.20以后的版本 18 | */ 19 | public class VersionControlAttribute : Attribute 20 | { 21 | /// 22 | /// 从a到b,写a..b;a和b,写a,b;什么都不写或没有这个标签,表示支持所有版本 23 | /// 24 | public string Range{ get; set; } 25 | } 26 | 27 | public class Version 28 | { 29 | public string OriginCode { get; set; } 30 | public List Codes { get; set; } 31 | public static Version FromString(string source) 32 | { 33 | return AnalyzeVersionCode(source); 34 | } 35 | private static Version AnalyzeVersionCode(string source) 36 | { 37 | try 38 | { 39 | Version version = new Version() { OriginCode = source }; 40 | Dictionary alphabetas = new Dictionary() 41 | { 42 | { "alpha", 1 }, 43 | { "beta", 2 } 44 | }; 45 | var codeStrs = source.Split(new[] { '.', '-' }); 46 | List codes = new List(); 47 | foreach (var code in codeStrs) 48 | { 49 | int tempCode = 0; 50 | if (int.TryParse(code.Trim(), out tempCode)) 51 | { 52 | codes.Add(tempCode); 53 | } 54 | else 55 | { 56 | var character = code.Trim().ToLower(); 57 | if (alphabetas.ContainsKey(character)) 58 | { 59 | codes.Add(alphabetas[character]); 60 | } 61 | } 62 | } 63 | version.Codes = codes; 64 | return version; 65 | } 66 | catch (Exception ex) 67 | { 68 | return null; 69 | } 70 | } 71 | 72 | /// 73 | /// 如果本身的版本号大,则1,如果小,则-1。相等0 74 | /// 75 | /// 76 | /// 77 | public int Compare(object obj) 78 | { 79 | if (obj == null) 80 | return int.MinValue; 81 | if(obj is Version) 82 | { 83 | var version = obj as Version; 84 | int minV = Math.Min(version.Codes.Count, this.Codes.Count); 85 | for(int i=0;i Codes[i]) 90 | return -1; 91 | else 92 | continue; 93 | } 94 | //如果还相等,则长的是大的 95 | if (Codes.Count > version.Codes.Count) 96 | return 1; 97 | else if (Codes.Count == version.Codes.Count) 98 | return 0; 99 | else 100 | return -1; 101 | } 102 | throw new InvalidVersionException(""); 103 | } 104 | public static bool operator <=(Version v1, Version v2) 105 | { 106 | var res = v1.Compare(v2); 107 | if (res == -1 || res == 0) 108 | return true; 109 | return false; 110 | } 111 | public static bool operator >=(Version v1, Version v2) 112 | { 113 | var res = v1.Compare(v2); 114 | if (res == 1 || res == 0) 115 | return true; 116 | return false; 117 | } 118 | //public static bool operator ==(Version v1, Version v2) 119 | //{ 120 | // if (v1 == null && v2 == null) 121 | // return true; 122 | // if (v1 == null && v2 != null) 123 | // return false; 124 | // var res = v1.Compare(v2); 125 | // if (res == 0) 126 | // return true; 127 | // return false; 128 | //} 129 | //public static bool operator !=(Version v1, Version v2) 130 | //{ 131 | // if (v1 == null && v2 == null) 132 | // return false; 133 | // if (v1 == null && v2 != null) 134 | // return true; 135 | // var res = v1.Compare(v2); 136 | // if (res != 0) 137 | // return true; 138 | // return false; 139 | //} 140 | 141 | public static Version MaxValue 142 | { 143 | get 144 | { 145 | return new Version() { Codes=new List() { int.MaxValue } }; 146 | } 147 | } 148 | public static Version MinValue 149 | { 150 | get 151 | { 152 | return new Version { Codes = new List() { int.MinValue } }; 153 | } 154 | } 155 | 156 | } 157 | 158 | public class VersionSeries 159 | { 160 | public VersionSeries() 161 | { 162 | Ranges = new List>(); 163 | Singles = new List(); 164 | } 165 | public static VersionSeries FromString(string source) 166 | { 167 | return AnalyzeVersion(source); 168 | } 169 | public bool IsBelongsToMe(Version version) 170 | { 171 | foreach(var range in Ranges) 172 | { 173 | if(range.Item1<=version && range.Item2>=version) 174 | { 175 | return true; 176 | } 177 | } 178 | return Singles.Exists(p => p == version); 179 | } 180 | public List> Ranges { get; set; } 181 | public List Singles { get; set; } 182 | public static VersionSeries AnySeries 183 | { 184 | get 185 | { 186 | VersionSeries series = new VersionSeries(); 187 | series.Ranges = new List>() 188 | { 189 | new Tuple(Version.MinValue,Version.MaxValue) 190 | }; 191 | return series; 192 | } 193 | } 194 | public bool IsAny() 195 | { 196 | return Ranges.Count == 1 197 | && Ranges[0].Item1.Compare(Version.MinValue) == 0 198 | && Ranges[0].Item2.Compare(Version.MaxValue) == 0; 199 | } 200 | 201 | private static VersionSeries AnalyzeVersion(string source) 202 | { 203 | if (string.IsNullOrEmpty(source) || source.Trim().Equals("*")) 204 | return VersionSeries.AnySeries; 205 | VersionSeries result = new VersionSeries(); 206 | var versionSegments = source.Split(','); 207 | foreach (var seg in versionSegments) 208 | { 209 | if (seg.Contains("..")) 210 | { 211 | Version lower, upper; 212 | var versions = seg.Split(new[] { ".." }, StringSplitOptions.RemoveEmptyEntries); 213 | if (versions.Count() >= 1) 214 | { 215 | lower = Version.FromString(versions[0]); 216 | if (versions.Count() >= 2) 217 | upper = Version.FromString(versions[1]); 218 | else 219 | upper = Version.MaxValue; 220 | result.Ranges.Add(new Tuple(lower, upper)); 221 | } 222 | } 223 | else 224 | { 225 | var version = Version.FromString(seg); 226 | if (version != null) 227 | result.Singles.Add(version); 228 | } 229 | } 230 | return result; 231 | } 232 | 233 | } 234 | 235 | } 236 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/CC.AutomatedTesting.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {81FBDF32-88AB-4FB5-8AF5-6A6D7C045214} 8 | Library 9 | Properties 10 | CC.AutomatedTesting.Infrastructure 11 | CC.AutomatedTesting.Infrastructure 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | true 36 | bin\x64\Debug\ 37 | DEBUG;TRACE 38 | full 39 | x64 40 | prompt 41 | MinimumRecommendedRules.ruleset 42 | 43 | 44 | bin\x64\Release\ 45 | TRACE 46 | true 47 | pdbonly 48 | x64 49 | prompt 50 | MinimumRecommendedRules.ruleset 51 | 52 | 53 | 54 | ..\packages\log4net.1.2.10.1\lib\net11\log4net.dll 55 | True 56 | 57 | 58 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 59 | True 60 | 61 | 62 | 63 | 64 | 65 | 66 | ..\packages\Microsoft.Tpl.Dataflow.4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll 67 | True 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | ..\packages\Selenium.WebDriver.3.6.0\lib\net45\WebDriver.dll 77 | True 78 | 79 | 80 | ..\packages\Selenium.Support.3.5.2\lib\net40\WebDriver.Support.dll 81 | True 82 | 83 | 84 | ..\packages\Wintellect.PowerThreading.1.0.0.0\lib\net35\Wintellect.PowerThreading.dll 85 | True 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | {379efc05-3c3b-4c2b-aed0-2de5c38ddf88} 144 | CC.AutomatedTesting.Reporting 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 此项目引用这台计算机上缺少的 NuGet 程序包。使用 NuGet 程序包还原可下载这些程序包。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 156 | 157 | 158 | 159 | 166 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Common/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CC.UPaaSCore.Common.Extensions 4 | { 5 | public static class DictionaryExtensions 6 | { 7 | /// 8 | /// 不存在则新增,存在则更新 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | public static Dictionary AddOrUpdate(this Dictionary dic, TKey key, TValue value) 17 | { 18 | lock (dic) 19 | { 20 | if (dic.ContainsKey(key)) 21 | dic[key] = value; 22 | else 23 | dic.Add(key, value); 24 | return dic; 25 | } 26 | } 27 | 28 | /// 29 | /// 存在,则不写入。不存在才写入 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | /// 36 | /// 37 | public static Dictionary TryAdd(this Dictionary dic, TKey key, TValue value) 38 | { 39 | if (!dic.ContainsKey(key)) 40 | dic.Add(key, value); 41 | return dic; 42 | } 43 | 44 | /// 45 | /// 尝试获取,如果不存在,则返回默认值 46 | /// 47 | /// 48 | /// 49 | /// 50 | /// 51 | /// 52 | /// 53 | public static TValue TryGetOrDefault(this Dictionary dic, TKey key, TValue value = default(TValue)) 54 | { 55 | if (!dic.ContainsKey(key)) 56 | return value; 57 | return dic[key]; 58 | } 59 | 60 | } 61 | } -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Common/RetryHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace CC.UPaaSCore.Common.Extensions 6 | { 7 | public static class RetryHelper 8 | { 9 | /// 10 | /// 必须保证里面的方法幂等性 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | public static List Execute(Action func, int retryTimes=1, int intervalSeconds=1) 17 | { 18 | if (retryTimes <= 0) 19 | retryTimes = 1; 20 | if (intervalSeconds <= 0 || intervalSeconds >= 1000) 21 | intervalSeconds = 1; 22 | List exList = new List(); 23 | for(int i = 0; i < retryTimes; i++) 24 | { 25 | try 26 | { 27 | func(); 28 | return null; 29 | } 30 | catch (Exception ex) 31 | { 32 | exList.Add(ex); 33 | Console.WriteLine($"重试{i}..."); 34 | Thread.Sleep(intervalSeconds); 35 | } 36 | } 37 | return exList; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Common/ValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CC.Meeting.DataConvert.CommonConvert 9 | { 10 | public static class ValueConverter 11 | { 12 | //todo Chenchen:加上null的处理;对于int、double、datetime等类型的default改为异常值,如int.min 13 | //todo Chenchen:加上object的扩展方法,使调用时用to即可 14 | public static T SafeConvert(object sourceText) 15 | { 16 | return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromString(sourceText.ToString()); 17 | } 18 | 19 | #region 20 | //public static T ConvertTo(object data) 21 | //{ 22 | // var dataStr = Convert.ToString(data); 23 | // if (typeof(T) == typeof(int)) 24 | // { 25 | // int temp; 26 | // return int.TryParse(dataStr, out temp) ? (T)(object)temp : Default(); 27 | // } 28 | // else if (typeof(T) == typeof(double)) 29 | // { 30 | // double temp; 31 | // return double.TryParse(dataStr, out temp) ? (T)(object)temp : Default(); 32 | // } 33 | // else if (typeof(T) == typeof(DateTime)) 34 | // { 35 | // DateTime temp = DateTime.MinValue; 36 | // return DateTime.TryParse(dataStr, out temp) ? (T)(object)temp : Default(); 37 | // } 38 | // else if (typeof(T) == typeof(long)) 39 | // { 40 | // long temp; 41 | // return long.TryParse(dataStr, out temp) ? (T)(object)temp : Default(); 42 | // } 43 | // else if (typeof(T) == typeof(bool)) 44 | // { 45 | // bool temp; 46 | // return bool.TryParse(dataStr, out temp) ? (T)(object)temp : Default(); 47 | // } 48 | // else if (typeof (T) == typeof (string)) 49 | // { 50 | // return (T)(object)dataStr; 51 | // } 52 | // return Default(); 53 | //} 54 | #endregion 55 | public static T To(this object source) 56 | { 57 | try 58 | { 59 | return SafeConvert(source); 60 | } 61 | catch 62 | { 63 | return Default(); 64 | } 65 | } 66 | public static T UnsafeTo(this object source) 67 | { 68 | try 69 | { 70 | return SafeConvert(source); 71 | } 72 | catch 73 | { 74 | throw new DataConvertException(typeof(T).FullName, source); 75 | } 76 | 77 | } 78 | 79 | 80 | private static Dictionary DefaultValueMapping = new Dictionary() 81 | { 82 | { typeof(int),default(int) }, 83 | { typeof(double),default(double) }, 84 | { typeof(long), default(long) }, 85 | { typeof(float),default(float) }, 86 | { typeof(DateTime),default(DateTime) }, 87 | { typeof(short),default(short) }, 88 | { typeof(ushort),default(ushort) }, 89 | { typeof(ulong),default(ulong) }, 90 | { typeof(uint),default(uint) }, 91 | { typeof(decimal),default(decimal) }, 92 | { typeof(bool),default(bool) } 93 | }; 94 | 95 | public static T Default() 96 | { 97 | if (DefaultValueMapping.ContainsKey(typeof(T))) 98 | return (T)DefaultValueMapping[typeof (T)]; 99 | return default(T); 100 | } 101 | 102 | 103 | public static bool Is(this string source) where T : struct 104 | { 105 | try 106 | { 107 | SafeConvert(source); 108 | return true; 109 | } 110 | catch 111 | { 112 | return false; 113 | } 114 | } 115 | 116 | 117 | } 118 | 119 | public class DataConvertException : Exception 120 | { 121 | public DataConvertException(string expectedType, object sourceValue) 122 | { 123 | ExpectedType = expectedType; 124 | SourceValue = Convert.ToString(sourceValue); 125 | } 126 | public string SourceValue { get; set; } 127 | public string ExpectedType { get; set; } 128 | public string ToUserInfo() 129 | { 130 | return "类型转换异常。期望类型:" + ExpectedType + ",原值:" + Convert.ToString(SourceValue); 131 | } 132 | public override string ToString() 133 | { 134 | return ToUserInfo() + "。 " + base.ToString(); 135 | } 136 | } 137 | 138 | 139 | 140 | } 141 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Constants.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 CC.AutomatedTesting.Infrastructure 8 | { 9 | public static class Constants 10 | { 11 | public static class Configs 12 | { 13 | public readonly static string AssertPrefix = "验证"; 14 | public readonly static string TestCasePrefix = "测试用例"; 15 | public readonly static string FrontEndSwitchPrefix = "前端版本"; 16 | public readonly static string FunctionCallPrefix = "调用函数"; 17 | 18 | public readonly static string NotaionPrefix = "//"; 19 | 20 | public readonly static string FuncPrefix = "函数"; 21 | 22 | } 23 | public readonly static char CommandParameterSpliter = ':'; 24 | public readonly static char ParameterSpliter = ','; 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/ConstructingContext.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.FunctionProcessor; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CC.AutomatedTesting.Infrastructure 9 | { 10 | public class ConstructingContext 11 | { 12 | public ConstructingContext() 13 | { 14 | Bindings = new Dictionary(); 15 | FunctionText = new List(); 16 | } 17 | public ConstructingContext(Dictionary binding, Version defaultVersion, Dictionary userFunctions=null) 18 | { 19 | Bindings = binding; 20 | DefaultFrontendVersion = defaultVersion; 21 | UserFunctions = userFunctions; 22 | } 23 | 24 | public Dictionary Bindings { get; set; } 25 | 26 | public Version DefaultFrontendVersion { get; set; } 27 | 28 | public Dictionary UserFunctions { get; set; } 29 | public List FunctionText { get; set; } 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Exceptions/BizException.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Bizbases.CommonActions; 2 | using OpenQA.Selenium; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.Exceptions 10 | { 11 | public class BizException:Exception 12 | { 13 | public BizException() 14 | { 15 | 16 | } 17 | public BizException(string message):base(message) 18 | { 19 | 20 | } 21 | public virtual string ToUserInfo() 22 | { 23 | return "出现业务异常:"+Message; 24 | } 25 | } 26 | 27 | public class ConstructingException: BizException 28 | { 29 | } 30 | 31 | public class RuntimeException: BizException 32 | { 33 | public RuntimeException():base() 34 | { 35 | 36 | } 37 | public RuntimeException(string actionName):base() 38 | { 39 | ActionName = actionName; 40 | } 41 | public RuntimeException(string actionName, string message) : base(message) 42 | { 43 | ActionName = actionName; 44 | } 45 | public string ActionName { get; set; } 46 | } 47 | public class InvalidCommandFormatException : ConstructingException 48 | { 49 | public string VariableName { get; private set; } 50 | public InvalidCommandFormatException(string variableName) 51 | { 52 | VariableName = variableName; 53 | } 54 | public override string ToUserInfo() 55 | { 56 | return "命令格式错误:" + VariableName; 57 | } 58 | } 59 | public class RedunantVariableDefinedException : ConstructingException 60 | { 61 | public string VariableName { get; private set; } 62 | public RedunantVariableDefinedException(string variableName) 63 | { 64 | VariableName = variableName; 65 | } 66 | public override string ToUserInfo() 67 | { 68 | return "重复定义的变量:"+VariableName; 69 | } 70 | } 71 | public class UnSpecifiedVersionException : ConstructingException 72 | { 73 | public UnSpecifiedVersionException() 74 | { 75 | } 76 | public override string ToUserInfo() 77 | { 78 | return "该测试用例未指定版本号。需指定全局版本号或用例版本号"; 79 | } 80 | } 81 | 82 | public class InvalidVersionException : ConstructingException 83 | { 84 | public string VersionStr { get; private set; } 85 | public InvalidVersionException(string versionStr) 86 | { 87 | VersionStr = versionStr; 88 | } 89 | public override string ToUserInfo() 90 | { 91 | return "不合法的版本号:" + VersionStr; 92 | } 93 | } 94 | 95 | public class CommandHandlerNotFound : ConstructingException 96 | { 97 | public CommandHandlerNotFound(string command) 98 | { 99 | CommandTitle = command; 100 | } 101 | public string CommandTitle { get; private set; } 102 | public override string ToUserInfo() 103 | { 104 | return $"未知命令:{CommandTitle} "; 105 | } 106 | } 107 | 108 | public class JavascriptRuntimeException:RuntimeException 109 | { 110 | public JavascriptRuntimeException(string actionName, string message):base(actionName) 111 | { 112 | } 113 | public override string ToUserInfo() 114 | { 115 | return $"JavaScript运行时异常:"+ Message; 116 | } 117 | } 118 | public class TimeOutRuntimeException : RuntimeException 119 | { 120 | public TimeOutRuntimeException(string actionName, string message) : base(actionName) 121 | { 122 | } 123 | 124 | public override string ToUserInfo() 125 | { 126 | return $"运行时等待超时异常:" + Message; 127 | } 128 | } 129 | 130 | public class WebElementNotFoundException : RuntimeException 131 | { 132 | public WebElementNotFoundException(string actionName, string elementDescription) : base(actionName) 133 | { 134 | ElementDescription = elementDescription; 135 | } 136 | 137 | public string ElementDescription { get; set; } 138 | public override string ToUserInfo() 139 | { 140 | return $"未找到指定元素:" + ElementDescription; 141 | } 142 | } 143 | 144 | public class WaitingElementTimeOutException : RuntimeException 145 | { 146 | public WaitingElementTimeOutException(By by) : base() 147 | { 148 | ByType = by; 149 | } 150 | public By ByType { get; set; } 151 | 152 | public override string ToUserInfo() 153 | { 154 | return $"等待查找元素超时:"+ ByType.ToString(); 155 | } 156 | } 157 | 158 | public class CouldNotFindCheckboxColumnInTableList : RuntimeException 159 | { 160 | public CouldNotFindCheckboxColumnInTableList() : base() 161 | { 162 | } 163 | public override string ToUserInfo() 164 | { 165 | return $"未能找到Checkbox列"; 166 | } 167 | } 168 | public class CouldNotFindCellValueInTableList : RuntimeException 169 | { 170 | public string ColumnTitle { get; set; } 171 | public string ColumnValue { get; set; } 172 | public CouldNotFindCellValueInTableList(string columnTitle, string columnValue) : base() 173 | { 174 | ColumnTitle = columnTitle; 175 | ColumnValue = columnValue; 176 | } 177 | public override string ToUserInfo() 178 | { 179 | return $"未能找到 {ColumnTitle} 列内的 {ColumnValue} "; 180 | } 181 | } 182 | 183 | public class ElementNotFoundException : RuntimeException 184 | { 185 | public ElementNotFoundException(string actionName, string elementTitle) : base(actionName) 186 | { 187 | ElementTitle = elementTitle; 188 | } 189 | public string ElementTitle { get; private set; } 190 | public override string ToUserInfo() 191 | { 192 | return $"未能找到{ElementTitle}"; 193 | } 194 | } 195 | public class IFrameNotFoundException : RuntimeException 196 | { 197 | public IFrameNotFoundException(string actionName ,string iframeId):base(actionName) 198 | { 199 | IframeId = iframeId; 200 | } 201 | public string IframeId { get; private set; } 202 | public override string ToUserInfo() 203 | { 204 | return $"未能找到id与{IframeId}匹配的iframe元素"; 205 | } 206 | } 207 | 208 | public class FunctionStructureException : RuntimeException 209 | { 210 | public FunctionStructureException(string message) 211 | { 212 | } 213 | 214 | public override string ToUserInfo() 215 | { 216 | return $"解析用户函数运行时异常:" + Message; 217 | } 218 | } 219 | public class FunctionParameterException : RuntimeException 220 | { 221 | public FunctionParameterException(string message) 222 | { 223 | } 224 | 225 | public override string ToUserInfo() 226 | { 227 | return $"用户函数参数异常:" + Message; 228 | } 229 | } 230 | 231 | public class FunctionNotFoundException:RuntimeException 232 | { 233 | public FunctionNotFoundException(string message) 234 | { 235 | } 236 | 237 | public override string ToUserInfo() 238 | { 239 | return $"未找到函数:" + Message; 240 | } 241 | } 242 | 243 | public class FunctionCallException : RuntimeException 244 | { 245 | public FunctionCallException(string message) 246 | { 247 | } 248 | 249 | public override string ToUserInfo() 250 | { 251 | return $"函数调用异常:" + Message; 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Extensions/StringEx.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.TextAnalyzer; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CC.AutomatedTesting.Infrastructure.Extensions 9 | { 10 | public static class StringEx 11 | { 12 | public static string[] SplitAndTrim(this string source, char splitter) 13 | { 14 | var result = source.Split(splitter); 15 | return result.Select(p => p.Trim()).ToArray(); 16 | } 17 | public static List Translate(this List source, Dictionary bindings) 18 | { 19 | List result = new List(); 20 | for (int i=0;i Escape(this List sources) 38 | { 39 | return sources.Select(p => Escaper.Process(p)).ToList(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Extensions/WebDriverEx.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | using OpenQA.Selenium.Support.UI; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace CC.AutomatedTesting.Infrastructure.Extensions 12 | { 13 | public static class WebDriverEx 14 | { 15 | public const string defaultPath = @"C:\CC.logfiles\自动化测试报表\截图\"; 16 | public static string ScreenShot(this IWebDriver webDriver, string fullPath=null) 17 | { 18 | var fileName = DateTime.Now.ToString("yyyyMMddHHmmss")+".jpg"; 19 | if(string.IsNullOrEmpty(fullPath)) 20 | { 21 | fullPath = defaultPath; 22 | } 23 | if(!Directory.Exists(fullPath)) 24 | { 25 | Directory.CreateDirectory(fullPath); 26 | } 27 | var fileFullName = defaultPath + fileName; 28 | Screenshot scrFile = ((ITakesScreenshot)webDriver).GetScreenshot(); 29 | scrFile.SaveAsFile(fileFullName, ScreenshotImageFormat.Jpeg); 30 | return fileFullName; 31 | } 32 | 33 | public static IList SafeFindElements(this ISearchContext element, By by, int maxSecondToWait = 10) 34 | { 35 | try 36 | { 37 | if (element == null || by == null) 38 | return null; 39 | if (maxSecondToWait <= 0) 40 | maxSecondToWait = 10; 41 | DateTime startTime = DateTime.Now; 42 | while (true) 43 | { 44 | var results = element.FindElements(by); 45 | if (results != null && results.Count > 0) 46 | return results; 47 | 48 | if ((DateTime.Now - startTime).TotalSeconds > maxSecondToWait) 49 | return new List(); 50 | Thread.Sleep(500); 51 | } 52 | } 53 | catch (Exception ex) 54 | { 55 | return null; 56 | } 57 | } 58 | 59 | public static IWebElement SafeFindElement(this ISearchContext element, By by, int maxSecondToWait = 10) 60 | { 61 | try 62 | { 63 | if (element == null || by == null) 64 | return null; 65 | if (maxSecondToWait <= 0) 66 | maxSecondToWait = 10; 67 | DateTime startTime = DateTime.Now; 68 | while (true) 69 | { 70 | var results = element.FindElements(by); 71 | if (results != null && results.Count > 0) 72 | return results.FirstOrDefault(); 73 | 74 | if ((DateTime.Now - startTime).TotalSeconds > maxSecondToWait) 75 | return null; 76 | Thread.Sleep(500); 77 | } 78 | } 79 | catch (Exception ex) 80 | { 81 | return null; 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Extensions/WebDriverWaitEx.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using OpenQA.Selenium; 3 | using OpenQA.Selenium.Support.UI; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace CC.AutomatedTesting.Infrastructure.Extensions 13 | { 14 | public static class WebDriverWaitEx 15 | { 16 | public static void UntilTrue(this WebDriverWait wait, Func checkFunction, int maxSeconds = 10) 17 | { 18 | DateTime startTime = DateTime.Now; 19 | while (true) 20 | { 21 | var funcResult = false; 22 | try 23 | { 24 | funcResult = checkFunction(); 25 | } 26 | catch 27 | { 28 | } 29 | if (funcResult) 30 | break; 31 | if ((DateTime.Now - startTime).TotalSeconds > maxSeconds) 32 | throw new TimeoutException("等待超时"); 33 | Thread.Sleep(500); 34 | } 35 | } 36 | 37 | public static bool UntilDisappear(this WebDriverWait wait, By by, IWebDriver webDriver, IWebElement parentContainer = null, int maxSecond = 60) 38 | { 39 | DateTime startTime = DateTime.Now; 40 | if (maxSecond <= 0) 41 | maxSecond = 10; 42 | while (true) 43 | { 44 | var elements = webDriver.FindElements(by); 45 | //如果找不到该元素 46 | if (elements.Count == 0) 47 | return true; 48 | //或者只有一个元素,且此元素的display属性还是none 49 | else if(elements.Count==1) 50 | { 51 | try 52 | { 53 | var displayValue = elements.FirstOrDefault().GetCssValue("display"); 54 | if (displayValue != null && displayValue == "none") 55 | return true; 56 | } 57 | catch (StaleElementReferenceException ex) 58 | {//如果出现异常(很有可能StaleElementReferenceException),就跳过本次循环,再重新找一次即可。 59 | } 60 | } 61 | 62 | if ((DateTime.Now - startTime).TotalSeconds > maxSecond) 63 | throw new TimeoutException("等待超时"); 64 | Thread.Sleep(500); 65 | } 66 | } 67 | 68 | public static IWebElement UntilFindElement(this WebDriverWait wait, By by, ISearchContext searchContext, int maxSecond = 60) 69 | { 70 | if (maxSecond <= 0) 71 | maxSecond = 10; 72 | DateTime startTime = DateTime.Now; 73 | while (true) 74 | { 75 | var elements = searchContext.FindElements(by); 76 | if (elements.Count > 0) 77 | return elements.FirstOrDefault(); 78 | if ((DateTime.Now - startTime).TotalSeconds > maxSecond) 79 | throw new WaitingElementTimeOutException(by); 80 | Thread.Sleep(500); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Extensions/WebElementEx.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.Extensions 10 | { 11 | public static class WebElementEx 12 | { 13 | /// 14 | /// level=0:返回本身,level=1:父元素(向上1个层级),level=2:父元素的父元素(向上2个层级) 15 | /// 16 | /// 17 | /// 18 | /// 19 | public static IWebElement Parent(this IWebElement element, int level=1) 20 | { 21 | if (level <= 0) 22 | return element; 23 | var xpath = string.Empty; 24 | for(int i=0;i DirectChild(this IWebElement element, string tagName) 38 | { 39 | return element.FindElements(By.XPath(tagName)); 40 | } 41 | 42 | public static string GetInnerText(this IWebElement element, IWebDriver webDriver) 43 | { 44 | var s= ((IJavaScriptExecutor)webDriver).ExecuteScript("return arguments[0].innerText", element); 45 | return s.ToString().Replace("\r\n",""); 46 | } 47 | public static void ClickMayBeHide(this IWebElement element, IWebDriver webDriver) 48 | { 49 | ((IJavaScriptExecutor)webDriver).ExecuteScript("arguments[0].click()", element); 50 | } 51 | public static void SetHiddenElementVisible(this IWebElement element, IWebDriver webDriver) 52 | { 53 | ((IJavaScriptExecutor)webDriver).ExecuteScript("arguments[0].style.display='block'", element); 54 | } 55 | 56 | public static IWebElement FindElementOrNull(this IWebElement element,By by) 57 | { 58 | try 59 | { 60 | return element.FindElement(by); 61 | } 62 | catch (Exception ex) 63 | { 64 | return null; 65 | } 66 | } 67 | public static IWebElement FindElementByMultipleBys(this IWebElement element, List bys) 68 | { 69 | foreach(var by in bys) 70 | { 71 | try 72 | { 73 | return element.FindElement(by); 74 | } 75 | catch (Exception ex) 76 | { 77 | continue; 78 | } 79 | } 80 | return null; 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/FunctionProcessor/FuncProcessor.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using CC.UPaaSCore.Common.Extensions; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.FunctionProcessor 10 | { 11 | public class UserFunctionDefinition 12 | { 13 | public string Name { get; internal set; } 14 | public List FormalParameters { get;internal set; } 15 | public List FormalBody { get; internal set; } 16 | /// 17 | /// 把函数定义,根据实参,替换形参,转成运行时状态 18 | /// 19 | /// 20 | /// 21 | public UserFunctionActual ToRuntime(List actualParameters) 22 | { 23 | if (actualParameters.Count != FormalParameters.Count) 24 | throw new FunctionParameterException("形参实参个数不一致"); 25 | UserFunctionActual ufRuntime = new UserFunctionActual(Name,actualParameters); 26 | foreach(var bodyStr in FormalBody) 27 | { 28 | var actualStr = bodyStr.Clone() as string; 29 | for(int i=0;i actualParameters) 41 | { 42 | Name = name; 43 | ActualParameters = actualParameters; 44 | ActualBody = new List(); 45 | } 46 | public string Name { get; } 47 | public List ActualParameters { get; } 48 | public List ActualBody { get; internal set; } 49 | } 50 | 51 | public class FuncProcessor 52 | { 53 | #region Signleton 54 | private static readonly FuncProcessor instance = new FuncProcessor(); 55 | private FuncProcessor() 56 | { 57 | } 58 | public static FuncProcessor Instance 59 | { 60 | get 61 | { 62 | return instance; 63 | } 64 | } 65 | #endregion 66 | /// 67 | /// 68 | /// 69 | /// 调用的实参 70 | /// 用户函数文本处理后的字符串 71 | /// 72 | public Dictionary> Process( List actualParam, List scriptStr) 73 | { 74 | //将函数转换成指令集 75 | return ConvertFuncToOrderSet(actualParam, scriptStr); 76 | } 77 | //拆分函数定义 78 | public Dictionary GetFunctionDefines(List scriptStr) 79 | { 80 | Dictionary result = new Dictionary(); 81 | var processedStr = scriptStr.Select(s => s.Replace("\n", "").Replace("\r", "").Replace("\t", "").Trim('{', '}').Replace(" ", "")); 82 | string joinStr = string.Join(" ", processedStr.ToArray()); 83 | //先拆分函数 84 | var funcList = joinStr.Split(new[] { "定义函数:" }, StringSplitOptions.RemoveEmptyEntries).ToList(); 85 | foreach (var func in funcList) 86 | { 87 | string[] funcArrary = func.Split(new[] { "(", ")" }, StringSplitOptions.None); 88 | if (funcArrary.Length >= 3) 89 | { 90 | var head = funcArrary[0] + "(" + funcArrary[1] + ")"; 91 | string formBody = func.Replace(head, ""); 92 | formBody = formBody.Substring(2, formBody.Length - 2); 93 | UserFunctionDefinition ufDefine = new UserFunctionDefinition() 94 | { 95 | Name = funcArrary[0].TrimEnd(':'), 96 | FormalParameters = funcArrary[1].Split(',').ToList(), 97 | FormalBody = formBody.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries).ToList() 98 | }; 99 | result.AddOrUpdate(ufDefine.Name, ufDefine); 100 | } 101 | else 102 | { 103 | throw new FunctionStructureException(string.Format("函数名称:{0}结构错误!请检查", funcArrary[0])); 104 | } 105 | } 106 | return result; 107 | } 108 | 109 | private Dictionary> ConvertFuncToOrderSet(List actualParam, List scriptStr) 110 | { 111 | Dictionary> result = new Dictionary>(); 112 | if (!actualParam.Any()) throw new Exception("用户函数调用时,实参为空!"); 113 | var processedStr = scriptStr.Select(s => s.Replace("\n", "").Replace("\r", "").Replace("\t", "").Trim('{', '}').Replace(" ", "")); 114 | string joinStr = string.Join(" ", processedStr.ToArray()); 115 | //先拆分函数 116 | var funcList = joinStr.Split(new[] { "定义函数:" }, StringSplitOptions.RemoveEmptyEntries).ToList(); 117 | foreach (var func in funcList) 118 | { 119 | string[] funcArrary = func.Split(new[] { "(", ")" }, StringSplitOptions.RemoveEmptyEntries); 120 | if (funcArrary.Length == 3) 121 | { 122 | var funcBodyStr = funcArrary[2]; 123 | string funcName = funcArrary[0].TrimEnd(':'); 124 | if (string.IsNullOrEmpty(funcArrary[1])) 125 | { 126 | throw new FunctionParameterException(string.Format("函数名称:{0}参数为空!请检查", funcArrary[0])); 127 | } 128 | Dictionary funcParam = new Dictionary(); 129 | string[] parameterArray = funcArrary[1].Split(','); 130 | if (actualParam.Count != parameterArray.Length) throw new Exception("用户函数调用时,实参与形参个数不一致!"); 131 | for (int index = 0; index < parameterArray.Length; index++) 132 | { 133 | funcParam.Add(parameterArray[index], actualParam[index]); 134 | //替换str中的形参 135 | funcBodyStr = funcBodyStr.Replace(parameterArray[index], actualParam[index]); 136 | } 137 | var commandList = funcBodyStr.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries).ToList(); 138 | result.Add(funcName, commandList); 139 | } 140 | else 141 | { 142 | continue; 143 | throw new FunctionStructureException(string.Format("函数名称:{0}结构错误!请检查", funcArrary[0])); 144 | } 145 | } 146 | return result; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Jquery/JQueryExecutor.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CC.AutomatedTesting.Infrastructure.Jquery 9 | { 10 | public class JQueryExecutor 11 | { 12 | private IWebDriver WebDriver { get; set; } 13 | 14 | public JQueryExecutor(IWebDriver webDriver) 15 | { 16 | WebDriver = webDriver; 17 | } 18 | public object ExecuteJquery(string jqueryScript) 19 | { 20 | InjectJqueryIfNeeded(); 21 | return ((IJavaScriptExecutor)WebDriver).ExecuteScript(jqueryScript); 22 | } 23 | 24 | 25 | //加载jquery 26 | private void InjectJqueryIfNeeded() 27 | { 28 | if (!JqueryIsLoaded()) 29 | { 30 | injectJQuery(); 31 | } 32 | } 33 | 34 | /// 35 | /// 判断是否加载jquery,返回true表示已加载jquery 36 | /// 37 | /// 38 | private Boolean JqueryIsLoaded() 39 | { 40 | try 41 | { 42 | return (Boolean)((IJavaScriptExecutor)WebDriver).ExecuteScript("return jQuery()!=null"); 43 | } 44 | catch (Exception e) 45 | { 46 | return false; 47 | } 48 | } 49 | 50 | /// 51 | /// 注入jquery 52 | /// 53 | private void injectJQuery() 54 | { 55 | //在head中拼出加载jquery的html,固定写法 56 | String jquery = "var headID=document.getElementsByTagName(\"head\")[0];" + 57 | "var newScript = document.createElement('script');" + 58 | "newScript.type='text/javascript';" + 59 | "newScript.src='https://cdn.rawgit.com/anshooarora/extentreports/jquery.js';" + 60 | "headID.appendChild(newScript);"; 61 | //执行js 62 | ((IJavaScriptExecutor)WebDriver).ExecuteScript(jquery); 63 | } 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Log/Computer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Configuration; 4 | using System.Linq; 5 | using System.Web; 6 | using System.Xml.Linq; 7 | using System.Management; 8 | 9 | /// 10 | ///Computer 的摘要说明 11 | /// 12 | public class Computer 13 | { 14 | public static string ipAddress; 15 | public static string IpAddress 16 | { 17 | get 18 | { 19 | if (string.IsNullOrEmpty(ipAddress)) 20 | ipAddress = GetIPAddress(); 21 | return ipAddress; 22 | } 23 | } 24 | 25 | 26 | public static string computerName; 27 | public static string ComputerName 28 | { 29 | get 30 | { 31 | if (string.IsNullOrEmpty(computerName)) 32 | computerName = GetComputerName(); 33 | return computerName; 34 | } 35 | } 36 | 37 | public static string loginUserName; 38 | public static string LoginUserName 39 | { 40 | get 41 | { 42 | if (string.IsNullOrEmpty(loginUserName)) 43 | loginUserName = GetUserName(); 44 | return loginUserName; 45 | } 46 | } 47 | 48 | public static string systemType; 49 | public static string SystemType 50 | { 51 | get 52 | { 53 | if (string.IsNullOrEmpty(systemType)) 54 | systemType = GetSystemType(); 55 | return systemType; 56 | } 57 | } 58 | 59 | 60 | public static string GetCpuID() 61 | { 62 | try 63 | { 64 | //获取CPU序列号代码 65 | string cpuInfo = "";//cpu序列号 66 | ManagementClass mc = new ManagementClass("Win32_Processor"); 67 | ManagementObjectCollection moc = mc.GetInstances(); 68 | foreach (ManagementObject mo in moc) 69 | { 70 | cpuInfo = mo.Properties["ProcessorId"].Value.ToString(); 71 | } 72 | moc = null; 73 | mc = null; 74 | return cpuInfo; 75 | } 76 | catch 77 | { 78 | return "unknow"; 79 | } 80 | finally 81 | { 82 | } 83 | 84 | } 85 | public static string GetMacAddress() 86 | { 87 | try 88 | { 89 | //获取网卡硬件地址 90 | string mac = ""; 91 | ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration"); 92 | ManagementObjectCollection moc = mc.GetInstances(); 93 | foreach (ManagementObject mo in moc) 94 | { 95 | if ((bool)mo["IPEnabled"] == true) 96 | { 97 | mac = mo["MacAddress"].ToString(); 98 | break; 99 | } 100 | } 101 | moc = null; 102 | mc = null; 103 | return mac; 104 | } 105 | catch 106 | { 107 | return "unknow"; 108 | } 109 | finally 110 | { 111 | } 112 | 113 | } 114 | public static string GetIPAddress() 115 | { 116 | try 117 | { 118 | //获取IP地址 119 | string st = ""; 120 | ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration"); 121 | ManagementObjectCollection moc = mc.GetInstances(); 122 | foreach (ManagementObject mo in moc) 123 | { 124 | if ((bool)mo["IPEnabled"] == true) 125 | { 126 | //st=mo["IpAddress"].ToString(); 127 | System.Array ar; 128 | ar = (System.Array)(mo.Properties["IpAddress"].Value); 129 | st = ar.GetValue(0).ToString(); 130 | break; 131 | } 132 | } 133 | moc = null; 134 | mc = null; 135 | return st; 136 | } 137 | catch 138 | { 139 | return "unknow"; 140 | } 141 | finally 142 | { 143 | } 144 | 145 | } 146 | public static string GetDiskID() 147 | { 148 | try 149 | { 150 | //获取硬盘ID 151 | String HDid = ""; 152 | ManagementClass mc = new ManagementClass("Win32_DiskDrive"); 153 | ManagementObjectCollection moc = mc.GetInstances(); 154 | foreach (ManagementObject mo in moc) 155 | { 156 | HDid = (string)mo.Properties["Model"].Value; 157 | } 158 | moc = null; 159 | mc = null; 160 | return HDid; 161 | } 162 | catch 163 | { 164 | return "unknow"; 165 | } 166 | finally 167 | { 168 | } 169 | 170 | } 171 | /// 172 | /// 操作系统的登录用户名 173 | /// 174 | /// 175 | public static string GetUserName() 176 | { 177 | try 178 | { 179 | string st = ""; 180 | ManagementClass mc = new ManagementClass("Win32_ComputerSystem"); 181 | ManagementObjectCollection moc = mc.GetInstances(); 182 | foreach (ManagementObject mo in moc) 183 | { 184 | 185 | st = mo["UserName"].ToString(); 186 | 187 | } 188 | moc = null; 189 | mc = null; 190 | return st; 191 | } 192 | catch 193 | { 194 | return "unknow"; 195 | } 196 | finally 197 | { 198 | } 199 | 200 | } 201 | /// 202 | /// PC类型 203 | /// 204 | /// 205 | public static string GetSystemType() 206 | { 207 | try 208 | { 209 | string st = ""; 210 | ManagementClass mc = new ManagementClass("Win32_ComputerSystem"); 211 | ManagementObjectCollection moc = mc.GetInstances(); 212 | foreach (ManagementObject mo in moc) 213 | { 214 | 215 | st = mo["SystemType"].ToString(); 216 | 217 | } 218 | moc = null; 219 | mc = null; 220 | return st; 221 | } 222 | catch 223 | { 224 | return "unknow"; 225 | } 226 | finally 227 | { 228 | } 229 | 230 | } 231 | /// 232 | /// 物理内存 233 | /// 234 | /// 235 | public static string GetTotalPhysicalMemory() 236 | { 237 | try 238 | { 239 | 240 | string st = ""; 241 | ManagementClass mc = new ManagementClass("Win32_ComputerSystem"); 242 | ManagementObjectCollection moc = mc.GetInstances(); 243 | foreach (ManagementObject mo in moc) 244 | { 245 | 246 | st = mo["TotalPhysicalMemory"].ToString(); 247 | 248 | } 249 | moc = null; 250 | mc = null; 251 | return st; 252 | } 253 | catch 254 | { 255 | return "unknow"; 256 | } 257 | finally 258 | { 259 | } 260 | } 261 | /// 262 | /// 263 | /// 264 | /// 265 | public static string GetComputerName() 266 | { 267 | try 268 | { 269 | return System.Environment.GetEnvironmentVariable("ComputerName"); 270 | } 271 | catch 272 | { 273 | return "unknow"; 274 | } 275 | finally 276 | { 277 | } 278 | } 279 | 280 | 281 | 282 | } 283 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/ParallelRun/TPLManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using System.Threading.Tasks.Dataflow; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.ParallelRun 10 | { 11 | public class TPLManager 12 | { 13 | #region Signleton 14 | private static readonly TPLManager instance = new TPLManager(); 15 | private TPLManager() 16 | { 17 | } 18 | public static TPLManager Instance 19 | { 20 | get 21 | { 22 | return instance; 23 | } 24 | } 25 | #endregion 26 | 27 | public ActionBlock abAsync = new ActionBlock((suite) => 28 | { 29 | Console.WriteLine(suite + " ThreadId:" + Thread.CurrentThread.ManagedThreadId + " Execute Time:" + DateTime.Now); 30 | TestSuiteRunner runner = new TestSuiteRunner();//调用Runner,执行Suite 31 | runner.Run(suite); 32 | } 33 | , new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 5 }); 34 | 35 | public void QueueTestSuite(TestSuite testSuite) 36 | { 37 | abAsync.Post(testSuite); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("CC.AutomatedTesting.Infrastructure")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("CC.AutomatedTesting.Infrastructure")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("81fbdf32-88ab-4fb5-8af5-6a6d7c045214")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/TestCase.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Bizbases; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure 10 | { 11 | public class TestCase 12 | { 13 | public TestCase() 14 | { 15 | OrdinaryActions = new List(); 16 | AssertActions = new List(); 17 | Actions = new ObservableCollection(); 18 | } 19 | public string TestSetID { get; set; }//一组测试用例的ID标识。用于输出时,把这些用例归到一起生成report 20 | public string Name { get; set; } 21 | public List OrdinaryActions { get; set; } 22 | public List AssertActions { get; set; } 23 | public ObservableCollection Actions { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/TestCaseExecuter.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.ActionFactory; 2 | using CC.AutomatedTesting.Infrastructure.Exceptions; 3 | using CC.AutomatedTesting.Infrastructure.Extensions; 4 | using CC.AutomatedTesting.Reporting; 5 | using OpenQA.Selenium; 6 | using OpenQA.Selenium.Chrome; 7 | using OpenQA.Selenium.Remote; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Diagnostics; 11 | using System.IO; 12 | using System.Linq; 13 | using System.Reflection; 14 | using System.Text; 15 | using System.Threading; 16 | using System.Threading.Tasks; 17 | 18 | namespace CC.AutomatedTesting.Infrastructure 19 | { 20 | /*TestCase结果规则: 21 | * 1. 如果某个Action(包括Perform和Assert)出现异常,则整个Case结果为Exception,并中断执行,返回结果 22 | * 2. 如果所有Action都没异常,会执行所有的PerformAction,之后执行所有的AssertAction,并记录所有AssertAction的结果 23 | * 3. 如果所有AssertAction结果都是OK,则整个Case结果为Pass;任一AssertAction不是OK,整个结果为Fail 24 | */ 25 | public class TestCaseExecuter 26 | { 27 | public TestCaseExecuter(IWebDriver _webDriver, ManualResetEvent _executerResetEvent) 28 | { 29 | WebDriver = _webDriver; 30 | ExecuterAutoResetEvent=_executerResetEvent; 31 | } 32 | public IWebDriver WebDriver { get; private set; } 33 | private ManualResetEvent ExecuterAutoResetEvent; 34 | 35 | public TestCaseResult RunPerformAction(PerformAction action) 36 | { 37 | try 38 | { 39 | action.WebDriver = WebDriver; 40 | action.SwitchToActionRequiredIframe(); 41 | action.Excuting(); 42 | action.PreConditionCheck(); 43 | action.Perform(); 44 | action.PostConditionCheck(); 45 | action.Success(); 46 | return new TestCaseResult() 47 | { 48 | Status = TestCaseResultStatus.Pass 49 | }; 50 | } 51 | catch (RuntimeException ex) 52 | { 53 | //action.Fail(ex); 54 | ex.ActionName = action.ActionName; 55 | //LogHelper.Error("RuntimeException Exception Happens:" + ex.ToString()); 56 | var screenShotAddress = WebDriver.ScreenShot(); 57 | //Case执行中,一旦发生异常,就退出,不再继续执行 58 | return new TestCaseResult() 59 | { 60 | Status = TestCaseResultStatus.Exception, 61 | ExceptionInfo = new Tuple(action, ex, screenShotAddress) 62 | }; 63 | } 64 | catch (Exception ex) 65 | { 66 | //action.Fail(ex); 67 | //LogHelper.Error("OrdinaryActions Exception Happens:" + ex.ToString()); 68 | var screenShotAddress = WebDriver.ScreenShot(); 69 | //Case执行中,一旦发生异常,就退出,不再继续执行 70 | return new TestCaseResult() 71 | { 72 | Status = TestCaseResultStatus.Exception, 73 | ExceptionInfo = new Tuple(action, ex, screenShotAddress) 74 | }; 75 | } 76 | } 77 | 78 | public AssertionResult RunAssertAction(AssertAction assertion) 79 | { 80 | try 81 | { 82 | assertion.WebDriver = WebDriver; 83 | assertion.SwitchToActionRequiredIframe(); 84 | assertion.Excuting(); 85 | assertion.PreConditionCheck(); 86 | AssertionResult currentResult = assertion.Assert();//当前这个断言的结果 87 | assertion.PostConditionCheck(); 88 | assertion.Success(); 89 | return currentResult; 90 | } 91 | catch (Exception ex)//用户自定义的异常 92 | { 93 | assertion.Fail(ex); 94 | Console.WriteLine("AssertActions Exception Happens:" + ex.ToString()); 95 | return null; 96 | //var screenShotAddress = webDriver.ScreenShot(); 97 | ////调用selennium进行截图。 98 | //return new TestCaseResult() 99 | //{ 100 | // Status = TestCaseResultStatus.Exception, 101 | // ExceptionInfo = new Tuple(assertion, ex, screenShotAddress), 102 | // ResultDetails = resultDetails 103 | //}; 104 | } 105 | } 106 | 107 | public TestCaseResult Run(TestCase testcase) 108 | { 109 | var tempCurrentIframePath = new List(); 110 | Dictionary resultDetails = new Dictionary(); 111 | Stopwatch stopWatch = new Stopwatch(); 112 | foreach (var action in testcase.Actions) 113 | { 114 | stopWatch.Start(); 115 | //ExecuterAutoResetEvent.WaitOne(); 116 | if (action is FunctionAction) 117 | { 118 | var funcAction = action as FunctionAction; 119 | Console.WriteLine("执行函数调用:" + funcAction.CommandText); 120 | foreach (var bodyaction in funcAction.BodyActions) 121 | { 122 | if (!(bodyaction is PerformAction)) 123 | continue; 124 | Console.WriteLine(" --执行:" + bodyaction.AnalyziedCommandText); 125 | var bodyPerformAction = bodyaction as PerformAction; 126 | RunPerformAction(bodyPerformAction); 127 | } 128 | } 129 | else if (action is PerformAction) 130 | { 131 | Console.WriteLine("执行:" + action.AnalyziedCommandText); 132 | var performAction = action as PerformAction; 133 | RunPerformAction(performAction); 134 | } 135 | else if (action is AssertAction) 136 | { 137 | Console.WriteLine("验证:" + action.AnalyziedCommandText); 138 | var assertAction = action as AssertAction; 139 | var assertResult = RunAssertAction(assertAction); 140 | if (assertResult != null) //断言正常结束:断言成功、失败 141 | { 142 | resultDetails.Add(assertAction, assertResult); 143 | if(!assertResult.IsOk) 144 | { 145 | action.ExecuteState = Action.ActionExecuteState.AssertionFail; 146 | Console.WriteLine($"断言验证失败。期望结果:{assertResult.ExpectedResult},实际结果{assertResult.ActualResult}"); 147 | //Console.WriteLine("点击任何按键继续"); 148 | //Console.ReadKey(); 149 | } 150 | else 151 | { 152 | action.ExecuteState = Action.ActionExecuteState.AssertionPass; 153 | Console.WriteLine("******断言验证通过******"); 154 | } 155 | } 156 | } 157 | if (action.ExecuteState == Action.ActionExecuteState.Failed) 158 | { 159 | Console.WriteLine($"命令执行失败:{action.Message}、\r\n堆栈信息\r\n:{action.InnerException.ToString()}"); 160 | Console.WriteLine("点击任何按键继续"); 161 | Console.ReadKey(); 162 | //暂停执行,卡住 163 | } 164 | stopWatch.Stop(); 165 | Console.WriteLine($"{action}命令执行时间为:{stopWatch.Elapsed}"); 166 | } 167 | 168 | return new TestCaseResult() 169 | { 170 | //如果验证结果有一个的IsOK为false,则整个Case结果为Fail 171 | Status = resultDetails.Values.Any(p => p.IsOk == false) ? TestCaseResultStatus.Fail : TestCaseResultStatus.Pass, 172 | ResultDetails = resultDetails 173 | }; 174 | } 175 | } 176 | 177 | public class TestCaseResult 178 | { 179 | //总体状态 180 | public TestCaseResultStatus Status { get; set; } 181 | //如果走到了断言,记录每个断言的结果 182 | public Dictionary ResultDetails { get; set; } 183 | //如果有异常,记录异常发生的Action、异常信息、截图地址 184 | public Tuple ExceptionInfo { get; set; } 185 | 186 | public string GenerateResultMessage() 187 | { 188 | if (Status == TestCaseResultStatus.Pass) 189 | { 190 | return ""; 191 | } 192 | else if (Status == TestCaseResultStatus.Fail) 193 | { 194 | return AssertionResult; 195 | } 196 | else if (Status == TestCaseResultStatus.Exception) 197 | { 198 | return ExceptionResult /*+ "\r\n验证执行结果:\r\n" + AssertionResult*/; 199 | } 200 | else if (Status == TestCaseResultStatus.Unexecuted) 201 | { 202 | return "未执行"; 203 | } 204 | return "未知"; 205 | } 206 | 207 | private string AssertionResult 208 | { 209 | get 210 | { 211 | if (ResultDetails != null) 212 | { 213 | return string.Join("\r\n", ResultDetails.Select(p => p.Key.CommandText + ":" + p.Value.ToString()).ToArray()); 214 | } 215 | return ""; 216 | } 217 | } 218 | private string ExceptionResult 219 | { 220 | get 221 | { 222 | if (ExceptionInfo != null) 223 | { 224 | var exceptionMessage = ExceptionInfo.Item2.Message; 225 | if (ExceptionInfo.Item2 is RuntimeException) 226 | { 227 | var ex = ExceptionInfo.Item2 as RuntimeException; 228 | return "执行 " + ExceptionInfo.Item1.CommandText + " 时,出现异常: " + ex.ToUserInfo() + " 。\r\n截图地址:" + ExceptionInfo.Item3; 229 | } 230 | return "执行 " + ExceptionInfo.Item1.CommandText + " 时,出现异常: " + ExceptionInfo.Item2.Message + "。\r\n截图地址:" + ExceptionInfo.Item3; 231 | } 232 | return ""; 233 | } 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/TestCaseFactory.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.ActionFactory; 2 | using CC.AutomatedTesting.Infrastructure.Exceptions; 3 | using CC.AutomatedTesting.Infrastructure.FunctionProcessor; 4 | using CC.UPaaSCore.Common.Extensions; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Collections.ObjectModel; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Text; 11 | using System.Text.RegularExpressions; 12 | using System.Threading.Tasks; 13 | 14 | namespace CC.AutomatedTesting.Infrastructure 15 | { 16 | #region 根据用户输入的Command,来创建TestCase 17 | /// 18 | /// 创建TestCase的工厂:根据用户命令,调用ActionFactory,来实现创建Action 19 | /// 20 | public class TestCaseFactory 21 | { 22 | #region Signleton 23 | private static readonly TestCaseFactory instance = new TestCaseFactory(); 24 | private TestCaseFactory() 25 | { 26 | } 27 | public static TestCaseFactory Instance 28 | { 29 | get 30 | { 31 | return instance; 32 | } 33 | } 34 | #endregion 35 | public TestCase CreateTestCase(string[] userCommands, string caseName, ConstructingContext context) 36 | { 37 | var testCase = new TestCase(); 38 | testCase.Name = caseName; 39 | Version version = context.DefaultFrontendVersion; 40 | testCase.Actions = ActionsFactory.Instance.CreateActions(userCommands, context); 41 | return testCase; 42 | } 43 | 44 | 45 | } 46 | #endregion 47 | 48 | public class FunctionFactory 49 | { 50 | #region Signleton 51 | private static readonly FunctionFactory instance = new FunctionFactory(); 52 | private FunctionFactory() 53 | { 54 | } 55 | public static FunctionFactory Instance 56 | { 57 | get 58 | { 59 | return instance; 60 | } 61 | } 62 | #endregion 63 | 64 | public FunctionAction CreateFunction(string command, ConstructingContext context) 65 | { 66 | //解析Command,获得函数名、实参列表 67 | var functionInfo = GetFunctionActualInfo(command); 68 | //如果之前解析的上下文中,没有该函数定义,则异常 69 | if (!context.UserFunctions.ContainsKey(functionInfo.Item1)) 70 | throw new FunctionNotFoundException(functionInfo.Item1); 71 | //找到函数定义,并根据实参列表,进行形参->实参转换 72 | var functionActual = context.UserFunctions[functionInfo.Item1].ToRuntime(functionInfo.Item2); //获得实参下的函数 73 | 74 | FunctionAction funcAction = new FunctionAction() { FunctionActual = functionActual, CommandText = command }; 75 | funcAction.BodyActions = ActionsFactory.Instance.CreateActions(functionActual.ActualBody.ToArray(), context); 76 | return funcAction; 77 | } 78 | private Tuple> GetFunctionActualInfo(string userCommand) 79 | { 80 | var commandSlices = userCommand.Trim().Split(':'); 81 | if (commandSlices.Length < 3) 82 | throw new FunctionCallException(userCommand); 83 | var functionName = commandSlices[1].Trim(); 84 | var actualParameters = commandSlices[2].Trim().Split(',').Select(p => p.Trim()).ToList(); 85 | return new Tuple>(functionName, actualParameters); 86 | } 87 | } 88 | 89 | public class ActionsFactory 90 | { 91 | #region Signleton 92 | private static readonly ActionsFactory instance = new ActionsFactory(); 93 | private ActionsFactory() 94 | { 95 | } 96 | public static ActionsFactory Instance 97 | { 98 | get 99 | { 100 | return instance; 101 | } 102 | } 103 | #endregion 104 | public ObservableCollection CreateActions(string[] commands, ConstructingContext context) 105 | { 106 | ObservableCollection actions = new ObservableCollection(); 107 | Version version = context.DefaultFrontendVersion; 108 | foreach (var command in commands) 109 | { 110 | if (IsFrontEndSwitch(command)) 111 | { 112 | var versionCodeStr = command.Trim().Replace(Constants.Configs.FrontEndSwitchPrefix, "").Replace(":", "").Trim(); 113 | version = Version.FromString(versionCodeStr); 114 | continue; 115 | } 116 | else if (IsFunctionCall(command)) 117 | { 118 | //FunctionFactory和ActionFactory存在循环依赖的可能。这取决于用户定义函数的时候,不能递归调用自身 119 | actions.Add(FunctionFactory.Instance.CreateFunction(command, context)); 120 | } 121 | else if (IsAssert(command)) 122 | { 123 | actions.Add(AssertActionFactory.Instance.CreateAction(command.Trim(), version, context)); 124 | //testCase.AssertActions.Add(AssertActionFactory.Instance.CreateAction(command.Trim(), version,context)); 125 | } 126 | else if (IsUserFunc(command)) 127 | { 128 | actions.Add(FuncActionFactory.Instance.CreateAction(command.Trim(), version, context)); 129 | } 130 | else 131 | { 132 | actions.Add(PerformActionFactory.Instance.CreateAction(command.Trim(), version, context)); 133 | //testCase.OrdinaryActions.Add(PerformActionFactory.Instance.CreateAction(command.Trim(),version,context)); 134 | } 135 | } 136 | return actions; 137 | } 138 | 139 | private bool IsAssert(string userCommand) 140 | { 141 | return userCommand.StartsWith(Constants.Configs.AssertPrefix); 142 | } 143 | 144 | private bool IsFrontEndSwitch(string userCommand) 145 | { 146 | return userCommand.StartsWith(Constants.Configs.FrontEndSwitchPrefix); 147 | } 148 | 149 | private bool IsFunctionCall(string userCommand) 150 | { 151 | return userCommand.StartsWith(Constants.Configs.FunctionCallPrefix); 152 | } 153 | 154 | private bool IsUserFunc(string userCommand) 155 | { 156 | return userCommand.StartsWith(Constants.Configs.FuncPrefix); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/TestSuite.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CC.AutomatedTesting.Infrastructure 9 | { 10 | public class TestSuite 11 | { 12 | public TestSuite() 13 | { 14 | TestCases = new ObservableCollection(); 15 | } 16 | public TestSuite(string suitename) 17 | { 18 | SuiteName = suitename; 19 | TestCases = new ObservableCollection(); 20 | } 21 | public ObservableCollection TestCases { get; set; } 22 | public string SuiteName { get; set; } 23 | public Version DefaultFrontendVersion { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/TestSuiteFactory.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.ActionFactory; 2 | using CC.AutomatedTesting.Infrastructure.FunctionProcessor; 3 | using CC.AutomatedTesting.Infrastructure.ParallelRun; 4 | using CC.AutomatedTesting.Infrastructure.TextAnalyzer; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Text; 10 | using System.Text.RegularExpressions; 11 | using System.Threading.Tasks; 12 | 13 | namespace CC.AutomatedTesting.Infrastructure 14 | { 15 | public class TestSuiteFactory 16 | { 17 | #region Signleton 18 | private static readonly TestSuiteFactory instance = new TestSuiteFactory(); 19 | private TestSuiteFactory() 20 | { 21 | } 22 | public static TestSuiteFactory Instance 23 | { 24 | get 25 | { 26 | return instance; 27 | } 28 | } 29 | #endregion 30 | 31 | public TestSuite CreateTestSuiteFrom(string[] userTestLines, string suiteName, Dictionary userFunctions) 32 | { 33 | TestSuite suite = new TestSuite(suiteName); 34 | var result = TextPreProcesser.Preprocess(userTestLines.ToList()); 35 | var defaultVersion = ExtractFrontEndVersion(result.Item1); 36 | ConstructingContext context = new ConstructingContext(result.Item2, defaultVersion.Item2,userFunctions); //保存构建上下文 37 | var splitedCaseLines = SplitTestCases(result.Item1); 38 | foreach (var caseTerm in splitedCaseLines) 39 | { 40 | if (caseTerm.Count == 0) 41 | continue; 42 | var title = GetCaseTitle(caseTerm[0]); 43 | caseTerm.RemoveAt(0); 44 | suite.TestCases.Add(TestCaseFactory.Instance.CreateTestCase(caseTerm.ToArray(), title, context)); 45 | } 46 | return suite; 47 | } 48 | 49 | public TestSuite CreateTestSuiteFrom(string[] userTestLines, string suiteName) 50 | { 51 | TestSuite suite = new TestSuite(suiteName); 52 | var result = TextPreProcesser.Preprocess(userTestLines.ToList()); 53 | var defaultVersion = ExtractFrontEndVersion(result.Item1); 54 | ConstructingContext context = new ConstructingContext(result.Item2, defaultVersion.Item2); //保存构建上下文 55 | var splitedCaseLines = SplitTestCases(defaultVersion.Item1); 56 | foreach (var caseTerm in splitedCaseLines) 57 | { 58 | if (caseTerm.Count == 0) 59 | continue; 60 | var title = GetCaseTitle(caseTerm[0]); 61 | caseTerm.RemoveAt(0); 62 | suite.TestCases.Add(TestCaseFactory.Instance.CreateTestCase(caseTerm.ToArray(), title, context)); 63 | } 64 | return suite; 65 | } 66 | private Tuple, Version> ExtractFrontEndVersion(List lines) 67 | { 68 | List refinedLines = lines.Select(p => p).ToList(); 69 | var pattern = @"^\s*前端版本\s*:\s*\S+"; 70 | var line = refinedLines.FirstOrDefault(p => Regex.IsMatch(p, pattern)); 71 | refinedLines.Remove(line); 72 | var versionCodeStr = line.Trim().Replace("前端版本", "").Replace(":", "").Trim(); 73 | Version version = Version.FromString(versionCodeStr); 74 | return new Tuple, Version>(refinedLines, version); 75 | } 76 | 77 | public List> SplitTestCases(List lines) 78 | { 79 | List> caseLines = new List>(); 80 | List tempCase = new List(); 81 | 82 | 83 | var refinedLines = lines.Where(p => !IsLineToSkip(p)) // Skip useless lines 84 | .Select(p => p.Trim()) //Trim All Lines 85 | .ToList(); 86 | 87 | int index = 1; 88 | foreach (var line in refinedLines) 89 | { 90 | // Test case name line 91 | if (refinedLines.Count == index || line.StartsWith(Constants.Configs.TestCasePrefix)) 92 | { 93 | if (refinedLines.Count == index) 94 | { 95 | tempCase.Add(line); 96 | } 97 | //When calling ToList, system will deep copy the selected element to a new List 98 | caseLines.Add(tempCase.ToList()); 99 | tempCase.Clear(); 100 | } 101 | tempCase.Add(line); 102 | index++; 103 | } 104 | return caseLines; 105 | } 106 | 107 | public string GetCaseTitle(string caseTitle) 108 | { 109 | if (caseTitle.Contains(Constants.CommandParameterSpliter)) 110 | { 111 | var spliterIndex = caseTitle.IndexOf(Constants.CommandParameterSpliter); 112 | return caseTitle.Substring(spliterIndex + 1, caseTitle.Length - spliterIndex - 1); 113 | } 114 | return caseTitle; 115 | } 116 | public bool IsLineToSkip(string line) 117 | { 118 | //Skip empty lines and notations 119 | return string.IsNullOrWhiteSpace(line) || line.StartsWith(Constants.Configs.NotaionPrefix); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/TestSuiteRunner.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.ActionFactory; 2 | using CC.AutomatedTesting.Infrastructure.ParallelRun; 3 | using CC.AutomatedTesting.Reporting; 4 | using OpenQA.Selenium; 5 | using OpenQA.Selenium.Chrome; 6 | using OpenQA.Selenium.Remote; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Reflection; 12 | using System.Text; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | 16 | namespace CC.AutomatedTesting.Infrastructure 17 | { 18 | public class TestSuiteRunner 19 | { 20 | public ITestReport ReportEngine { get; set; } 21 | public bool IsPaused { get; private set; } = false; 22 | 23 | private readonly static TaskFactory RunnerTaskFactory = new TaskFactory(); 24 | private ManualResetEvent RunnerResetEvent; 25 | private TestCaseExecuter Executer; 26 | //应用内只有一个WebDriver 27 | private static IWebDriver webDriver; 28 | private static IWebDriver WebDriver 29 | { 30 | get 31 | { 32 | if(webDriver==null) 33 | webDriver = WebDriverFactory.GetInstance(WebDriverType.LocalChrome); 34 | return webDriver; 35 | } 36 | } 37 | /// 38 | /// 执行TestSuite。返回Report的结果 39 | /// 40 | /// 41 | /// 42 | public void Run(TestSuite testSuite) 43 | { 44 | //RunnerTaskFactory.StartNew(() => 45 | //{ 46 | // RunnerResetEvent = new ManualResetEvent(true); 47 | using (ReportEngine = ReportFactory.CreateInstance(testSuite.SuiteName)) 48 | { 49 | Executer = new TestCaseExecuter(WebDriver, RunnerResetEvent); 50 | bool isProcessBreak = false; 51 | foreach (var testCase in testSuite.TestCases) 52 | { 53 | if (!isProcessBreak) 54 | { 55 | //可能结果:Pass、Fail、Fatal 或者抛出异常 56 | var caseResult = Executer.Run(testCase); 57 | //需要扩展方法:把caseResult的Exception、Details直接TOString() 58 | ReportEngine.RecordCase(testCase.Name, caseResult.Status, caseResult.GenerateResultMessage()); 59 | //当断言验证失败时,还继续执行,不中断 60 | if (caseResult.Status == TestCaseResultStatus.Fail || caseResult.Status == TestCaseResultStatus.Exception) 61 | { 62 | isProcessBreak = true; 63 | } 64 | } 65 | else 66 | { 67 | ReportEngine.RecordCase(testCase.Name, TestCaseResultStatus.Unexecuted, ""); 68 | } 69 | //先判断isProcessBreak==true?。如果是,则执行Case。不是,直接打Unexecuted结果 70 | //TODO:对于Pass的Case,调用ReportEngine,记录一个Pass结果 71 | // 对于Fail的Case,调用ReportEngine,记录一个Fail结果。并终止所有case的执行,isProcessBreak=true,把未执行的标记为Unexecuted。 72 | // 对于Exception的Case,调用ReportEngine,记录一个Fatal结果。并终止所有case的执行,isProcessBreak=true,把未执行的标记为Unexecuted。 73 | // 对于isProcessBreak=true,调用ReportEngine,把所有的标记为Unexecuted 74 | } 75 | } 76 | //}); 77 | } 78 | 79 | public void Pause() 80 | { 81 | IsPaused = true; 82 | RunnerResetEvent?.Reset(); 83 | } 84 | 85 | public void Continue() 86 | { 87 | IsPaused = false; 88 | RunnerResetEvent?.Set(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/TextAnalyzer/Analyzer.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.TextAnalyzer 10 | { 11 | public class TextPreProcesser 12 | { 13 | /// 14 | /// 预处理:删掉空白、注释行。解析变量定义 15 | /// 16 | /// 原脚本 17 | /// Item1:预处理后剩下的脚本;Item2:上下文 18 | public static Tuple,Dictionary> Preprocess(List text) 19 | { 20 | var refinedText = new List(); 21 | refinedText = EatComment(text); 22 | refinedText = EatEmptyLines(refinedText); 23 | return ExtractVariables(refinedText); 24 | 25 | } 26 | public static Tuple, Dictionary, List> PreprocessV2(List text) 27 | { 28 | var refinedText = new List(); 29 | refinedText = EatComment(text); 30 | refinedText = EatEmptyLines(refinedText); 31 | return ExtractVariablesV2(refinedText); 32 | 33 | } 34 | 35 | 36 | public static List EatComment(List text) 37 | { 38 | string commentPrefix = "\\\\"; 39 | return text.Where(p => !p.StartsWith(commentPrefix)).ToList(); 40 | } 41 | public static List EatEmptyLines(List text) 42 | { 43 | List textList = new List(); 44 | foreach (var textItem in text) 45 | { 46 | if (!string.IsNullOrEmpty(textItem.Trim())) 47 | { 48 | textList.Add(textItem.Trim().Replace(" ", "").Replace("\n", "").Replace("\r", "").Replace("\t", "")); 49 | } 50 | } 51 | return textList; 52 | } 53 | 54 | public static Tuple, Dictionary> ExtractVariables(List text) 55 | { 56 | List variableLines = new List(); 57 | var refinedText = EatVariableDefines(text, out variableLines); 58 | Dictionary variableMapping = Explain(variableLines); 59 | return new Tuple, Dictionary>(refinedText,variableMapping); 60 | } 61 | public static Tuple, Dictionary, List> ExtractVariablesV2(List text) 62 | { 63 | List variableLines = new List(); 64 | var refinedText = EatVariableDefines(text, out variableLines); 65 | Dictionary variableMapping = Explain(variableLines); 66 | List funcOrderSet = GetFuncTextSection(refinedText).ToList(); 67 | var removeFuncText = refinedText.Except(funcOrderSet)?.ToList(); 68 | return new Tuple, Dictionary, List>(removeFuncText, variableMapping, funcOrderSet); 69 | } 70 | 71 | private static List GetFuncTextSection(List text) 72 | { 73 | List funcTextList = new List(); 74 | List startIndexList = new List(); 75 | List endIndexList = new List(); 76 | for (int index = 0; index < text.Count; index++) 77 | { 78 | if (text[index].StartsWith("定义函数:")) 79 | { 80 | startIndexList.Add(index); 81 | } 82 | if (text[index].Equals("}")) 83 | { 84 | endIndexList.Add(index); 85 | } 86 | } 87 | if(startIndexList.Count != endIndexList.Count) 88 | { 89 | throw new Exception("函数格式不正确,注意函数体的闭合!"); 90 | } 91 | for (int index = 0; index < startIndexList.Count; index++) 92 | { 93 | funcTextList.AddRange(text.GetRange(startIndexList[index], endIndexList[index] - startIndexList[index] + 1)); 94 | } 95 | return funcTextList; 96 | } 97 | 98 | public static List EatVariableDefines(List text, out List defineLines) 99 | { 100 | string regex = @"^\s*定义\s*:\s*[^=\s]+\s*=\s*\S+\s*$"; 101 | defineLines= text.Where(p => Regex.IsMatch(p, regex)).ToList(); 102 | return text.Where(p => !Regex.IsMatch(p, regex)).ToList(); 103 | } 104 | 105 | /// 106 | /// 解析参数定义。 输入: 员工姓名=墨竹@随机数; 输入: Key:员工姓名,Value:墨竹09192 107 | /// 108 | /// 109 | /// 110 | public static Dictionary Explain(List defineLines) 111 | { 112 | Dictionary result = new Dictionary(); 113 | var timeStamp = DateTime.Now.ToString("yyyyMMddHHmmss"); 114 | var randomSeed = DateTime.Now.Subtract(new DateTime(2010, 1, 1)).Seconds; 115 | Random ran = new Random(randomSeed); 116 | var randomNumber = ran.Next(99999).ToString(); 117 | string regexVariableName = @":\s*[^=\s]+\s*="; 118 | string regexValue = @"=\s*\S+\s*$"; 119 | foreach(var line in defineLines) 120 | { 121 | var namePart = Regex.Match(line, regexVariableName).Value; 122 | namePart = namePart.Substring(1, namePart.Length - 2).Trim(); 123 | var valuePart = Regex.Match(line, regexValue).Value; 124 | valuePart = valuePart.Substring(1, valuePart.Length - 1).Trim(); 125 | valuePart = valuePart.Replace("@时间戳", timeStamp); 126 | valuePart = valuePart.Replace("@随机数", randomNumber); 127 | if (result.ContainsKey(namePart)) 128 | { 129 | throw new RedunantVariableDefinedException(namePart); 130 | } 131 | result.Add(namePart, valuePart); 132 | } 133 | return result; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/TextAnalyzer/Escaper.cs: -------------------------------------------------------------------------------- 1 | using CC.AutomatedTesting.Infrastructure.Exceptions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Infrastructure.TextAnalyzer 10 | { 11 | public class Escaper 12 | { 13 | public static Dictionary EscapeTable = new Dictionary() 14 | { 15 | { "&cma","," } 16 | }; 17 | public static string Process(string source) 18 | { 19 | var result = source.Clone().ToString(); 20 | foreach(var kv in EscapeTable) 21 | { 22 | result = result.Replace(kv.Key, kv.Value); 23 | } 24 | return result; 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/WebDriverFactory.cs: -------------------------------------------------------------------------------- 1 | using OpenQA.Selenium; 2 | using OpenQA.Selenium.Chrome; 3 | using OpenQA.Selenium.Remote; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CC.AutomatedTesting.Infrastructure 11 | { 12 | public class WebDriverFactory 13 | { 14 | private static readonly string remoteHubUrl = "http://10.189.1.206:4444/wd/hub"; 15 | public static IWebDriver GetInstance(WebDriverType type) 16 | { 17 | switch (type) 18 | { 19 | case WebDriverType.RemoteChrome: 20 | return new RemoteWebDriver(new Uri(remoteHubUrl), new ChromeOptions()); 21 | case WebDriverType.LocalChrome: 22 | ChromeOptions options = new ChromeOptions(); 23 | ////忽略Https证书不存在的错误,有助于加快Https页面加载速度 24 | options.AddArgument("--ignore-certificate-errors"); 25 | options.AddArgument("--start-maximized"); 26 | var webDriver = new ChromeDriver(options); 27 | //webDriver.Manage().Window.Maximize(); 28 | return webDriver; 29 | default: 30 | return null; 31 | } 32 | } 33 | } 34 | public enum WebDriverType 35 | { 36 | LocalChrome=1, 37 | RemoteChrome=2, 38 | LocalFirefox=3, 39 | RemoteFirefox=4, 40 | LocalIE=5, 41 | RemoteIE=6 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Infrastructure/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Reporting/CC.AutomatedTesting.Reporting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {379EFC05-3C3B-4C2B-AED0-2DE5C38DDF88} 8 | Library 9 | Properties 10 | CC.AutomatedTesting.Reporting 11 | CC.AutomatedTesting.Reporting 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | true 34 | bin\x64\Debug\ 35 | DEBUG;TRACE 36 | full 37 | x64 38 | prompt 39 | MinimumRecommendedRules.ruleset 40 | 41 | 42 | bin\x64\Release\ 43 | TRACE 44 | true 45 | pdbonly 46 | x64 47 | prompt 48 | MinimumRecommendedRules.ruleset 49 | 50 | 51 | 52 | ..\packages\DnsClient.1.0.7\lib\net45\DnsClient.dll 53 | 54 | 55 | ..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll 56 | True 57 | 58 | 59 | ..\packages\MongoDB.Bson.2.7.2\lib\net45\MongoDB.Bson.dll 60 | 61 | 62 | ..\packages\MongoDB.Driver.2.7.2\lib\net45\MongoDB.Driver.dll 63 | 64 | 65 | ..\packages\MongoDB.Driver.Core.2.7.2\lib\net45\MongoDB.Driver.Core.dll 66 | 67 | 68 | ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll 69 | 70 | 71 | ..\packages\RazorEngine.3.10.0\lib\net45\RazorEngine.dll 72 | 73 | 74 | ..\packages\ExtentReports.2.41.0\lib\RelevantCodes.ExtentReports.dll 75 | 76 | 77 | 78 | ..\packages\System.Buffers.4.3.0\lib\netstandard1.1\System.Buffers.dll 79 | 80 | 81 | 82 | ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.0.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll 83 | True 84 | 85 | 86 | ..\packages\Microsoft.AspNet.Razor.3.0.0\lib\net45\System.Web.Razor.dll 87 | True 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 113 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Reporting/ExtentReport.cs: -------------------------------------------------------------------------------- 1 | using RelevantCodes.ExtentReports; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CC.AutomatedTesting.Reporting 10 | { 11 | //业务需求: 12 | // 1. 提供一个方法,记录Case是失败还是成功。失败的话,可以写原因。 13 | // (规定:Fatal级别是异常导致的,Fail是验证失败导致的) 14 | // 2. 周围设置:可以改报告存放地址、环境、启动用户名字、 15 | // 其他操作,用户不需感知。 16 | 17 | class ExtentReport : ITestReport 18 | { 19 | private string reportDirectory = "C:\\CC.logfiles\\自动化测试报表\\"; 20 | public ExtentReports extent; 21 | public ExtentTest test; 22 | public string ReportPath { get; private set; } 23 | public string ReportName { get; private set; } 24 | public string ReportFullName => ReportPath + ReportName; 25 | #region 构造方法 26 | public ExtentReport(string reportName) 27 | { 28 | //ReportPath = GetDefaultReportPath(); 29 | ReportPath = reportDirectory; 30 | ReportName = DateTime.Now.ToString("yyyyMMddHHmmss")+".html"; 31 | if (!Directory.Exists(ReportPath)) 32 | { 33 | Directory.CreateDirectory(ReportPath); 34 | } 35 | extent = new ExtentReports(ReportPath+ReportName, true); 36 | } 37 | public ExtentReport(string fileName, string filePath, bool replaceExisting=true) 38 | { 39 | if (!filePath.EndsWith("\\")) 40 | filePath = filePath + "\\"; 41 | if (!Directory.Exists(filePath)) 42 | Directory.CreateDirectory(filePath); 43 | if (!fileName.EndsWith(".html",StringComparison.OrdinalIgnoreCase)) 44 | fileName += ".html"; 45 | ReportName = fileName; 46 | ReportPath = filePath; 47 | extent = new ExtentReports(ReportPath + ReportName, true); 48 | 49 | } 50 | /// 51 | /// 返回当前应用程序的所在目录。通常是在Bin文件下面 52 | /// 53 | /// 54 | private string GetDefaultReportPath() 55 | { 56 | string path = System.Reflection.Assembly.GetCallingAssembly().CodeBase; 57 | string projectPath = new Uri(path).LocalPath; 58 | return projectPath.Substring(0,projectPath.LastIndexOf('\\')) + "Reports\\"; 59 | } 60 | #endregion 61 | 62 | #region 接口内容 63 | public void Dispose() 64 | { 65 | if (extent != null) 66 | { 67 | if (test != null) 68 | { 69 | extent.EndTest(test); 70 | } 71 | extent.Flush(); 72 | extent.Close(); 73 | } 74 | } 75 | public ITestReport SetEnvironment(string environment) 76 | { 77 | throw new NotImplementedException(); 78 | } 79 | 80 | public void RecordCase(string testCaseName, TestCaseResultStatus status, string message = "") 81 | { 82 | test = extent.StartTest(testCaseName); 83 | test.Log(status.ToExtentStatus(), message); 84 | extent.EndTest(test); 85 | } 86 | #endregion 87 | } 88 | 89 | public static class ExtentReportEx 90 | { 91 | public static LogStatus ToExtentStatus(this TestCaseResultStatus status) 92 | { 93 | switch (status) 94 | { 95 | case TestCaseResultStatus.Pass: 96 | return LogStatus.Pass; 97 | case TestCaseResultStatus.Exception: 98 | return LogStatus.Fatal; 99 | case TestCaseResultStatus.Fail: 100 | return LogStatus.Fail; 101 | case TestCaseResultStatus.Unexecuted: 102 | return LogStatus.Skip; 103 | } 104 | return LogStatus.Unknown; 105 | } 106 | } 107 | 108 | //ExtentReport使用DEMO 109 | //public class ReportHelper 110 | //{ 111 | // public ExtentReports extent; 112 | // public ExtentTest test; 113 | 114 | 115 | 116 | // public void StartReport() 117 | // { 118 | // string path = System.Reflection.Assembly.GetCallingAssembly().CodeBase; 119 | // string actualPath = path.Substring(0, path.LastIndexOf("bin")); 120 | // string projectPath = new Uri(actualPath).LocalPath; 121 | // string reportPath = projectPath + "Reports\\MyOwnReport.html"; 122 | 123 | // extent = new ExtentReports(reportPath, true); 124 | // extent 125 | // .AddSystemInfo("Host Name", "Snow Host") 126 | // .AddSystemInfo("Environment", "Testing") 127 | // .AddSystemInfo("User Name", "Snow Forever"); 128 | // extent.LoadConfig(projectPath + "extent-config.xml"); 129 | // } 130 | 131 | // public void DoTest() 132 | // { 133 | // test = extent.StartTest("DemoReportPass"); 134 | // test.Log(LogStatus.Pass, "Assert Pass as condition is True"); 135 | // test = extent.StartTest("DemoReportFail"); 136 | // test.Log(LogStatus.Fail, "Assert Pass as condition is Fail"); 137 | // //Error、Fail、Fatal、Info、Pass、Skip、Unknown、Warning 138 | // extent.EndTest(test); 139 | // extent.Flush(); 140 | // extent.Close(); 141 | // } 142 | //} 143 | } 144 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Reporting/IReport.cs: -------------------------------------------------------------------------------- 1 | using RelevantCodes.ExtentReports; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CC.AutomatedTesting.Reporting 9 | { 10 | //业务需求: 11 | // 1. 提供一个方法,记录Case是失败还是成功。失败的话,可以写原因。 12 | // (规定:Fatal级别是异常导致的,Fail是验证失败导致的) 13 | // 2. 周围设置:可以改报告存放地址、环境、启动用户名字、 14 | // 其他操作,用户不需感知。 15 | public enum TestCaseResultStatus 16 | { 17 | Unexecuted = 0, 18 | Pass =1, 19 | Fail=2, 20 | Exception =3 21 | } 22 | public interface ITestReport:IDisposable 23 | { 24 | void RecordCase(string testCaseName, TestCaseResultStatus status, string message=""); 25 | 26 | ITestReport SetEnvironment(string environment); 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Reporting/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("Reporting")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("Reporting")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("379efc05-3c3b-4c2b-aed0-2de5c38ddf88")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Reporting/ReportFactory.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 CC.AutomatedTesting.Reporting 8 | { 9 | public class ReportFactory 10 | { 11 | public static ITestReport CreateInstance(string reportTitle) 12 | { 13 | //本Dll对外返回的类 14 | return new ExtentReport(reportTitle); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CC.AutomatedTesting.Reporting/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2019, Lucas20029 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutomatedTesting 2 | 基于自然语言驱动的自动化测试框架 3 | 4 | 例如:只需要提供一个如下脚本: 5 | 6 | ```C# 7 | 测试用例:登录贴吧 8 | 登录贴吧: ever_snowing,abcdefg 9 | ``` 10 | 就可以实现访问贴吧首页、输入用户名密码、点击登录的测试过程。 11 | --------------------------------------------------------------------------------