├── .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 |
--------------------------------------------------------------------------------