Beta.cs
34.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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
84
85
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
/************************************************************************************************
* All code in this file is under the MS-RL License (https://opensource.org/licenses/MS-RL) *
* By using the code in this file in any way, you agree to the above license terms. *
* Copyright (C) LIGHTS IN LINE AB (https://www.lightsinline.se) *
* Repository, Wiki, Issue tracker and more at https://git.lightsinline.se/products/VSTT-Plugins *
* *
* Contributors *
* LIGHTS IN LINE AB *
* SWEDBANK AB *
* SKATTEVERKET *
************************************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting.Rules;
using System.Text.RegularExpressions;
using System.IO;
using System.IO.Compression;
using Microsoft.VisualStudio.TestTools.LoadTesting;
using System.Net;
namespace LIL_VSTT_Plugins
{
[DisplayName("Convert Date Parameter to Age")]
[Description("Converts the date in given parameters into todays age and saves with suffix as new parameter")]
public class ConvertDateToAge : WebTestPlugin
{
[DisplayName("Parameter RegEx"), DefaultValue(""), Description("Regular expression. If the parameter exists in context and matches it will be convertet into a date. Will not fail the script if date cannot be converted.")]
public string ParameterRegEx { get; set; }
[DisplayName("Date Format"), DefaultValue("yyyy-MM-dd"), Description("The format of the date string in the parameter to convert from, see https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings")]
public string DateFormat { get; set; }
[DisplayName("Age Parameter Suffix"), DefaultValue("_Age"), Description("The suffix to be added for new parameters. If set to null or empty the original parameter will be overwritten.")]
public string Suffix { get; set; }
Regex regex = null;
public override void PreWebTest(object sender, PreWebTestEventArgs e)
{
base.PreWebTest(sender, e);
if (regex == null) regex = new Regex(ParameterRegEx);
Dictionary<string, object> temp = new Dictionary<string, object>();
bool convertSuccess = false;
// Iterate through all context variables
foreach (KeyValuePair<string, object> obj in e.WebTest.Context)
{
if(regex.IsMatch(obj.Key))
{
try
{
// Try to convert to a date using the specified format
DateTime parsedDate;
convertSuccess = DateTime.TryParseExact(obj.Value.ToString(), DateFormat, null, System.Globalization.DateTimeStyles.None, out parsedDate);
// Save the age
if (convertSuccess) temp.Add(obj.Key + Suffix, CalculateAge(parsedDate));
else e.WebTest.AddCommentToResult("Failed to calculate age using \"" + obj.Value.ToString() + "\" of parameter \"" + obj.Key + "\"" + " and format \"" + DateFormat + "\"");
}
catch (Exception ex)
{
e.WebTest.AddCommentToResult("Failed to convert date parameter to age: " + ex.Message);
}
}
}
// Add the new ones to the webtest context
foreach (KeyValuePair<string, object> obj in temp)
{
e.WebTest.Context[obj.Key] = obj.Value;
e.WebTest.AddCommentToResult("Saved value \"" + obj.Value + "\" into \"" + obj.Key + "\"");
}
}
public static int CalculateAge(DateTime birthDay)
{
int years = DateTime.Now.Year - birthDay.Year;
if ((birthDay.Month > DateTime.Now.Month) || (birthDay.Month == DateTime.Now.Month && birthDay.Day > DateTime.Now.Day))
years--;
return years;
}
}
[DisplayName("Force UrlEncoded Cookies")]
[Description("Reparses all cookies set by the server and stores the value in UrlEncoded format. Cookies with illegal characters will otherwise cause none of the previous set cookies to be added to new requests.")]
public class DebugCookies : WebTestPlugin
{
public override void PostRequest(object sender, PostRequestEventArgs e)
{
if (e.ResponseExists)
{
for (int i = 0; i < e.Response.Headers.Count; i++)
{
if (e.Response.Headers.Keys[i].Equals("Set-Cookie"))
{
try
{
System.Net.CookieContainer cc = new System.Net.CookieContainer();
cc.SetCookies(e.Response.ResponseUri, e.Response.Headers[i]);
foreach (Cookie c in cc.GetCookies(e.Response.ResponseUri))
{
c.Value = WebUtility.UrlEncode(WebUtility.UrlDecode(c.Value));
e.WebTest.Context.CookieContainer.Add(c);
}
}
catch (Exception ex)
{
e.WebTest.AddCommentToResult("Set-Cookie: Exception: " + ex.Message);
}
}
}
}
foreach (WebTestRequest w in e.Request.DependentRequests)
{
w.PostRequest += new EventHandler<PostRequestEventArgs>(this.PostRequest);
}
}
}
[DisplayName("Add Session Cookie")]
[Description("Adds a cookie to the webtest cookie collection for this request and all remaining requests in this session.")]
public class AddSessionCookie : WebTestRequestPlugin
{
[DisplayName("Cookie Name"), DefaultValue(""), Description("Name of the cookie to set.")]
public string CookieName { get; set; }
[DisplayName("Cookie Value"), DefaultValue(""), Description("Value of the cookie to set.")]
public string CookieValue { get; set; }
[DisplayName("Cookie Value Parameter Name"), DefaultValue(""), Description("Parameter for the cookie value. If the parameter is not found, the Cookie Value will be used instead.")]
public string CookieParameter { get; set; }
[DisplayName("Cookie Path"), DefaultValue("/"), Description("Path for the cookie to set. Cookie will only be sent if the URL Path matches.")]
public string CookiePath { get; set; }
[DisplayName("Cookie Domain"), DefaultValue(""), Description("Domain for the cookie to set. Cookie will only be sent if the host name matches.")]
public string CookieDomain { get; set; }
[DisplayName("Skip URL Encode"), DefaultValue(false), Description("By default the cookie value is URL Encoded for transfer. This will turn it off.")]
public bool SkipURLEncode { get; set; }
public override void PreRequestDataBinding(object sender, PreRequestDataBindingEventArgs e)
{
if (!String.IsNullOrEmpty(CookieParameter) && e.WebTest.Context.ContainsKey(CookieParameter))
CookieValue = e.WebTest.Context[CookieParameter].ToString();
if (!SkipURLEncode)
{
CookieValue = System.Net.WebUtility.UrlEncode(CookieValue);
CookieName = System.Net.WebUtility.UrlEncode(CookieName);
}
e.WebTest.Context.CookieContainer.Add(new System.Net.Cookie(CookieName, CookieValue, CookiePath, CookieDomain));
}
}
[DisplayName("Add Session Cookie")]
[Description("Adds a cookie to the webtest cookie collection for this test and all of its requests.")]
public class AddTestSessionCookie : WebTestPlugin
{
[DisplayName("Cookie Name"), DefaultValue(""), Description("Name of the cookie to set.")]
public string CookieName { get; set; }
[DisplayName("Cookie Value"), DefaultValue(""), Description("Value of the cookie to set.")]
public string CookieValue { get; set; }
[DisplayName("Cookie Value Parameter Name"), DefaultValue(""), Description("Parameter for the cookie value. If the parameter is not found, the Cookie Value will be used instead.")]
public string CookieParameter { get; set; }
[DisplayName("Cookie Path"), DefaultValue("/"), Description("Path for the cookie to set. Cookie will only be sent if the URL Path matches.")]
public string CookiePath { get; set; }
[DisplayName("Cookie Domain"), DefaultValue(""), Description("Domain for the cookie to set. Cookie will only be sent if the host name matches.")]
public string CookieDomain { get; set; }
[DisplayName("Skip URL Encode"), DefaultValue(false), Description("By default the cookie value is URL Encoded for transfer. This will turn it off.")]
public bool SkipURLEncode { get; set; }
public override void PreWebTest(object sender, PreWebTestEventArgs e)
{
if (!String.IsNullOrEmpty(CookieParameter) && e.WebTest.Context.ContainsKey(CookieParameter))
CookieValue = e.WebTest.Context[CookieParameter].ToString();
if (!SkipURLEncode)
{
CookieValue = System.Net.WebUtility.UrlEncode(CookieValue);
CookieName = System.Net.WebUtility.UrlEncode(CookieName);
}
e.WebTest.Context.CookieContainer.Add(new System.Net.Cookie(CookieName, CookieValue, CookiePath, CookieDomain));
}
}
[DisplayName("Add Parameter To Reporting Name")]
[Description("This request plugin will add the specified parameter to the end of the reporting name of the request")]
public class AddParameterToReportingName : WebTestRequestPlugin
{
[DisplayName("Parameter Name"), DefaultValue(""), Description("Name of the parameter which value should be added to the reporting name.")]
public string ParameterName { get; set; }
[DisplayName("Add to empty Report Name"), DefaultValue(false), Description("The parameter value will be added to report name even if it not set. Warning, will also set all redirects to the same name.")]
public bool AddToEmptyReportName { get; set; }
[DisplayName("Run on dependent requests"), DefaultValue(false), Description("The parameter value will be set as the report name on all dependent requests (only works in loadtests)")]
public bool RunOnDependents { get; set; }
public override void PreRequest(object sender, PreRequestEventArgs e)
{
base.PreRequest(sender, e);
if (e.WebTest.Context.ContainsKey(ParameterName) && (!String.IsNullOrEmpty(e.Request.ReportingName) || AddToEmptyReportName))
{
e.Request.ReportingName = e.Request.ReportingName + e.WebTest.Context[ParameterName].ToString();
}
}
public override void PostRequest(object sender, PostRequestEventArgs e)
{
base.PostRequest(sender, e);
if (RunOnDependents && e.WebTest.Context.ContainsKey(ParameterName))
{
foreach (WebTestRequest w in e.Request.DependentRequests)
{
w.ReportingName = e.WebTest.Context[ParameterName].ToString();
}
}
}
}
[DisplayName("Stop Here After")]
[Description("Add this plugin to a request in order to force the webtest to stop after the request has finished.")]
public class StopHereAfter : WebTestRequestPlugin
{
[DisplayName("Fail the test"), DefaultValue(true), Description("If set to true will fail the test iteration.")]
public bool FailTest { get; set; }
public override void PostRequest(object sender, PostRequestEventArgs e)
{
base.PostRequest(sender, e);
e.WebTest.AddCommentToResult("STOP HERE AFTER: WebTest will stop after the next request because of Request Plugin 'Stop Here After' was added to it.");
if (FailTest)
{
e.WebTest.Outcome = Outcome.Fail;
e.WebTest.AddCommentToResult("FAIL THE TEST: WebTest will fail after the next request because of option to fail the test was set to true in the plugin.");
}
e.WebTest.Stop();
}
}
[DisplayName("Zip File Upload"), Description("Creates an ZIP archive of each of the files to be uploaded using the files name and adding .zip. Warning, uses %TEMP% for temp storage.")]
public class ZipFileUploadBeforePost : WebTestRequestPlugin
{
Queue<String> deleteDirs = new Queue<string>();
public override void PreRequest(object sender, PreRequestEventArgs e)
{
if (e.Request.Body.GetType() == typeof(FormPostHttpBody)) {
FormPostHttpBody body = (FormPostHttpBody)e.Request.Body;
foreach (FormPostParameter param in body.FormPostParameters) {
if(param.GetType() == typeof(FileUploadParameter))
{
FileUploadParameter fparam = (FileUploadParameter)param;
String tempDir = Path.GetTempPath() + "\\" + Guid.NewGuid().ToString();
Directory.CreateDirectory(tempDir);
deleteDirs.Enqueue(tempDir);
Directory.CreateDirectory(tempDir + @"\ZipDir");
File.Copy(fparam.FileName, tempDir + @"\ZipDir\" + Path.GetFileName(fparam.FileName));
ZipFile.CreateFromDirectory(tempDir + @"\ZipDir", tempDir + "\\" + Path.GetFileName(fparam.FileName) + ".zip");
fparam.FileName = tempDir + "\\" + Path.GetFileName(fparam.FileName) + ".zip";
fparam.FileUploadName = fparam.FileUploadName + ".zip";
}
}
}
base.PreRequest(sender, e);
}
public override void PostRequest(object sender, PostRequestEventArgs e)
{
foreach (String dir in deleteDirs) Directory.Delete(dir, true);
deleteDirs.Clear();
base.PostRequest(sender, e);
}
}
[DisplayName("Set Request Think Time"), Description("Changes the thinktime on requests with a set thinktime over 0 or any given value to the value of the ThinkTime context parameter")]
public class SetRequestThinkTime : WebTestPlugin
{
[DisplayName("Debug"), DefaultValue(false), Description("Debug logging when thinktime is set on requests")]
public bool DebugMode { get; set; }
[DisplayName("Modify If Over (sec)"), DefaultValue(0), Description("Only modify the request think time if over this value. Default is 0.")]
public int ModifyOver { get; set; }
public override void PreRequest(object sender, PreRequestEventArgs e)
{
WebTestContext ctx = e.WebTest.Context;
if (ctx.ContainsKey("ThinkTime"))
{
int tt = Int32.Parse(ctx["ThinkTime"].ToString());
if (e.Request.ThinkTime > ModifyOver)
{
if (DebugMode) e.WebTest.AddCommentToResult("Setting Think Time to " + tt);
e.Request.ThinkTime = tt;
}
}
}
}
[DisplayName("Clear Cookies"), Description("Clears all cookies from previous iterations of this webtest")]
public class ClearCookies : WebTestPlugin
{
public override void PreWebTest(object sender, PreWebTestEventArgs e)
{
e.WebTest.Context.CookieContainer = new System.Net.CookieContainer();
}
}
[DisplayName("Think Time Emulator 10/190"), Description("Sets a context parameter named ThinkTime in each starting test to a random value between 10%-190% of the specified value.")]
public class ThinkTimeEmulator10190 : ILoadTestPlugin
{
[DisplayName("ThinkTime"), DefaultValue(35), Description("The Think Time to be used seconds. Default is 35.")]
public int ThinkTime { get; set; }
[DisplayName("Minimum"), DefaultValue(10), Description("Percentage of ThinkTime to be used as the minimum value")]
public int Min { get; set; }
[DisplayName("Maximum"), DefaultValue(190), Description("Percentage of ThinkTime to be used as the maximum value")]
public int Max { get; set; }
[DisplayName("Only on Tests matching"), DefaultValue(""), Description("Regular expression matching only the tests you want this plugin instance to work on. If it does not match the test name, the plugin instance will not set the ThinkTime")]
public string RegExTestName { get; set; }
[DisplayName("Only on Scenarios matching"), DefaultValue(""), Description("Regular expression matching only the scenarios you want this plugin instance to work on. If it does not match the scenario name, the plugin instance will not set the ThinkTime")]
public string RegExScenarioName { get; set; }
//store the load test object.
LoadTest mLoadTest;
Random rnd = new Random();
Regex rxTestName = null;
Regex rxScenarioName = null;
public void Initialize(LoadTest loadTest)
{
mLoadTest = loadTest;
if(!String.IsNullOrEmpty(RegExTestName))
rxTestName = new Regex(RegExTestName);
if (!String.IsNullOrEmpty(RegExScenarioName))
rxScenarioName = new Regex(RegExScenarioName);
// Sanity checks
if (ThinkTime == 0) ThinkTime = 35;
if (Min > Max) { Min = 100; Max = 100; }
if (Min == 0) Min = 10;
if (Max == 0) Max = 190;
//connect to the TestStarting event.
mLoadTest.TestStarting += new EventHandler<TestStartingEventArgs>(mLoadTest_TestStarting);
}
void mLoadTest_TestStarting(object sender, TestStartingEventArgs e)
{
if (rxTestName != null && rxTestName.IsMatch(e.TestName) != true) return;
if (rxScenarioName != null && rxScenarioName.IsMatch(e.ScenarioName) != true) return;
// Set the think time parameter in the tests context to a new value
double tt = ThinkTime;
double min = Min / 100d;
min = tt * min;
double max = Max / 100d;
max = tt * max;
e.TestContextProperties.Add("ThinkTime", rnd.Next((int)min, (int)max));
}
}
[DisplayName("Set Cookie from Query String"), Description("Sets a cookie with given name from the value of a given Query String parameter if found in the current request")]
public class SetCookieFromQueryString : WebTestPlugin
{
[DisplayName("Cookie name"), Description("Name of the cookie to set")]
public String CookieName { get; set; }
[DisplayName("Query String parameter name"), Description("Name of the query string parameter to look for")]
public String ParamName { get; set; }
public override void PreRequest(object sender, PreRequestEventArgs e)
{
base.PreRequest(sender, e);
QueryStringParameterCollection col = e.Request.QueryStringParameters;
for(int x=0; x < col.Count; x++)
{
if (col[x].Name == ParamName)
{
e.Request.Cookies.Add(new System.Net.Cookie(CookieName, col[x].Value));
return;
}
}
}
public override void PostRequest(object sender, PostRequestEventArgs e)
{
base.PostRequest(sender, e);
if (e.Request.HasDependentRequests)
{
foreach (WebTestRequest item in e.Request.DependentRequests)
{
QueryStringParameterCollection col = item.QueryStringParameters;
for (int x = 0; x < col.Count; x++)
{
if (col[x].Name == ParamName)
{
item.Cookies.Add(new System.Net.Cookie(CookieName, col[x].Value));
}
}
}
}
}
}
[DisplayName("Add Header"), Description("Adds the specified header to all requests matching a given URL regex")]
public class AddHeader : WebTestPlugin
{
[DisplayName("Header Name"), Description("Name of the header to set")]
public String HeaderName { get; set; }
[DisplayName("Header Value"), Description("Value of the header to set")]
public String HeaderValue { get; set; }
[DisplayName("URL RegEx"), Description("Regular Expression to match on the URL. Empty matches all requests.")]
public String RegEx { get; set; }
public override void PreRequest(object sender, PreRequestEventArgs e)
{
base.PreRequest(sender, e);
if (Regex.Match(e.Request.Url, RegEx).Success || RegEx.Length == 0)
{
e.Request.Headers.Add(HeaderName, HeaderValue);
}
}
public override void PostRequest(object sender, PostRequestEventArgs e)
{
base.PostRequest(sender, e);
if (e.Request.HasDependentRequests)
{
foreach (WebTestRequest item in e.Request.DependentRequests)
{
if (Regex.Match(item.Url, RegEx).Success || RegEx.Length == 0)
{
item.Headers.Add(HeaderName, HeaderValue);
}
}
}
}
}
[DisplayName("Log Context Arrays to File"), Description("Logs the specified context parameter arrays to file after each test iteration, one row for each array value")]
public class LogContextArrayToFile : WebTestPlugin
{
[DisplayName("Parameter arrays"), Description("Comma separated list of array parameters to log")]
public String Params { get; set; }
[DisplayName("File Name"), Description("The file name to use for logging")]
public String Filename { get; set; }
private bool header = true;
private string[] paramlist = null;
public override void PostWebTest(object sender, PostWebTestEventArgs e)
{
if (Params.Length == 0 || Filename.Length == 0) return;
if (paramlist == null) paramlist = Params.Split(',');
if (header)
{
File.AppendAllText(Filename, Params + "\r\n");
header = false;
}
// Check that the first param array has a count
int count = 0;
WebTestContext ctx = e.WebTest.Context;
if (ctx.ContainsKey(paramlist[0] + "_count")) count = Int32.Parse(ctx[paramlist[0] + "_count"].ToString());
for (int i = 1; i <= count; i++)
{
string row = "";
foreach (string param in paramlist)
{
if (row.Length > 0) row += ",";
if (ctx.ContainsKey(param + "_" + i)) row += ctx[param + "_" + i].ToString();
}
File.AppendAllText(Filename, row + "\r\n");
}
base.PostWebTest(sender, e);
}
}
[DisplayName("Log Context to File"), Description("Logs the specified context parameters to file after each test iteration")]
public class LogContextToFile : WebTestPlugin
{
[DisplayName("Regular Expression"), Description("The RegEx to use to match context parameter names")]
public String myRegex { get; set; }
[DisplayName("File Name"), Description("The file name to use for logging")]
public String Filename { get; set; }
[DisplayName("Autogenerate File Name"), Description("Automatically generate a filename using the test name and user id from the load test")]
public bool autoFilename { get; set; }
[DisplayName("Write header"), DefaultValue(false), Description("Writes the parameter names as a header. Will write a new header for each user (dont use in loadtest)")]
public bool useHeader { get; set; }
[DisplayName("Log if passed"), DefaultValue(true), Description("Logs the parameters if the webtest passed")]
public bool logPassed { get; set; }
[DisplayName("Log if failed"), DefaultValue(true), Description("Logs the parameters if the webtest failed")]
public bool logFailed { get; set; }
private bool header = true;
public override void PostWebTest(object sender, PostWebTestEventArgs e)
{
String completeFileName = Filename + e.WebTest.Context.WebTestUserId + ".log";
if (autoFilename)
{
completeFileName = e.WebTest.Name + e.WebTest.Context.WebTestUserId + ".log";
}
if ((e.WebTest.Outcome == Outcome.Pass && logPassed) || (e.WebTest.Outcome == Outcome.Fail && logFailed)) {
string row = "";
string hrow = "";
foreach (KeyValuePair<string, object> pair in e.WebTest.Context)
{
if (Regex.Match(pair.Key, myRegex).Success)
{
if (header && useHeader)
{
if (hrow.Length == 0) hrow = pair.Key.ToString();
else hrow += "," + pair.Key.ToString();
}
if (row.Length == 0) row = pair.Value.ToString();
else row += "," + pair.Value.ToString();
}
}
if (header && useHeader)
{
File.AppendAllText(completeFileName, hrow + "\r\n");
header = false;
}
File.AppendAllText(completeFileName, row + "\r\n");
base.PostWebTest(sender, e);
}
}
}
[DisplayName("Multi Regular Expression"), Description("Saves all matches for a regular expression including a count")]
public class MultiRegExExtract : ExtractionRule
{
[DisplayName("Regular Expression"), Description("The RegEx to use, supports grouping, for example (.+?)")]
public String Regex { get; set; }
[DisplayName("Group Name"), DefaultValue("1"), Description("The group to use as the value for the parameter, default is 1")]
public String GroupName { get; set; }
[DisplayName("Pass if not found"), DefaultValue(false), Description("Wherever this extration rule should pass even if no matches are found. Default false.")]
public bool PassIfNotFound { get; set; }
public override void Extract(object sender, ExtractionEventArgs e)
{
MatchCollection matches = System.Text.RegularExpressions.Regex.Matches(
e.Response.BodyString,
Regex,
RegexOptions.IgnoreCase);
int index = 0;
foreach (Match match in matches)
{
index++;
e.WebTest.Context[ContextParameterName + "_" + index] = match.Groups[GroupName].Value;
}
if (index > 0)
{
e.WebTest.Context[ContextParameterName + "_count"] = index.ToString();
e.Success = true;
e.Message = "Found " + index + " matches";
}
else
{
e.WebTest.Context[ContextParameterName + "_count"] = index.ToString();
e.Success = PassIfNotFound;
e.Message = "No matches found. Body size is " + e.Response.BodyString.Length;
}
}
}
[DisplayName("Extract Count"), Description("Counts all matches for a given RegEx and saves the count to the specified parameter")]
public class ExtractCount : ExtractionRule
{
[DisplayName("Regular Expression"), Description("The RegEx to use")]
public String Regex { get; set; }
[DisplayName("Fail on zero count"), DefaultValue(false), Description("Wherever this extration rule should fail if no matches are found")]
public bool FailOnZero { get; set; }
public override void Extract(object sender, ExtractionEventArgs e)
{
MatchCollection matches = System.Text.RegularExpressions.Regex.Matches(
e.Response.BodyString,
Regex,
RegexOptions.IgnoreCase);
e.WebTest.Context[ContextParameterName] = matches.Count.ToString();
e.Message = "Found " + matches.Count + " matches";
if (FailOnZero && matches.Count == 0)
{
e.Success = false;
}
}
}
[DisplayName("Extract Regular Expression From Dependents"), Description("Extracts data matching a regular expression from dependents belonging to this request")]
public class ExtractRegularExpressionFromResources : WebTestRequestPlugin
{
[DisplayName("Match Dependents"), Description("If specified, this regular expression must match the dependent. If empty, the extraction rule will be applied to all dependents.")]
public String DepRegex { get; set; }
[DisplayName("Context Parameter Name"), Description("Parameter name for the Extraction Rule")]
[Category("Extract Regular Expression Rule")]
public String ContextParameterName { get; set; }
[DisplayName("Regular Expression"), Description("Regular Expression for the Extraction rule")]
[Category("Extract Regular Expression Rule")]
public String RegularExpression { get; set; }
[DisplayName("Ignore Case"), Description("Ignore Case for the Extraction rule")]
[Category("Extract Regular Expression Rule")]
[DefaultValue(false)]
public bool IgnoreCase { get; set; }
[DisplayName("Required"), Description("Required for the Extraction rule")]
[Category("Extract Regular Expression Rule")]
[DefaultValue(true)]
public bool Required { get; set; }
[DisplayName("Index"), Description("Index for the Extraction rule")]
[Category("Extract Regular Expression Rule")]
[DefaultValue(0)]
public int Index { get; set; }
[DisplayName("Html Decode"), Description("Html Decode for the Extraction rule")]
[Category("Extract Regular Expression Rule")]
[DefaultValue(true)]
public bool HtmlDecode { get; set; }
[DisplayName("Use Groups"), Description("Use Groups for the Extraction rule")]
[Category("Extract Regular Expression Rule")]
[DefaultValue(false)]
public bool UseGroups { get; set; }
public override void PostRequest(object sender, PostRequestEventArgs e)
{
base.PostRequest(sender, e);
Regex rx = null;
if(!String.IsNullOrEmpty(DepRegex))
{
rx = new Regex(DepRegex);
}
// We cant do this here, replaced by SetResponseBodyCaptureLimitWebTestPlugin
//e.WebTest.ResponseBodyCaptureLimit = 10000000;
if (e.Request.HasDependentRequests)
{
foreach (WebTestRequest r in e.Request.DependentRequests)
{
if (rx != null && rx.IsMatch(r.Url))
{
ExtractionRuleReference ef = new ExtractionRuleReference(typeof(ExtractRegularExpression));
ef.ContextParameterName = ContextParameterName;
ef.DisplayName = "AutoExtractDisplayName";
ef.Description = "AutoExtractDescription";
ef.ExecutionOrder = RuleExecutionOrder.BeforeDependents;
ef.Properties.Add("RegularExpression", RegularExpression);
ef.Properties.Add("IgnoreCase", IgnoreCase.ToString());
ef.Properties.Add("Required", Required.ToString());
ef.Properties.Add("Index", Index.ToString());
ef.Properties.Add("HtmlDecode", HtmlDecode.ToString());
ef.Properties.Add("UseGroups", UseGroups.ToString());
r.ExtractionRuleReferences.Add(ef);
}
}
}
}
}
[DisplayName("Response Body Capture Limit"), Description("Sets the Response Body Byte Capture Limit for this WebTest. Warning, this can increase measured response times. Observe the difference in time to first byte and response time.")]
public class SetResponseBodyCaptureLimit : WebTestPlugin
{
[DisplayName("Limit Bytes"), Description("The number of bytes to set the limit to. Default value is 1.5 MB")]
[DefaultValue(1572864)]
public int Limit { get; set; }
public override void PreWebTest(object sender, PreWebTestEventArgs e)
{
e.WebTest.ResponseBodyCaptureLimit = Limit;
}
}
[DisplayName("Regular Expression Loop"), Description("Loop Condition that matches once on each regexp in previous response body")]
public class RegExpLoop : ConditionalRule
{
private Int32 CurrentMatch { get; set; }
private String LastUrl { get; set; }
private MatchCollection Matches { get; set; }
[DisplayName("Context parameter"), IsContextParameterName(true),
Description("Name of context parameter where the current value should be set in each loop")]
public string ContextParameterName { get; set; }
[DisplayName("Regex"), Description("The RegEx to use, supports grouping, for example (.+?)")]
public String Regex { get; set; }
[DisplayName("GroupName"), Description("The group to use as the value for the parameter if several are specified, default is 1")]
public String GroupName { get; set; }
// Methods
public override void CheckCondition(object sender, ConditionalEventArgs e)
{
if (CurrentMatch < Matches.Count)
{
e.WebTest.Context[ContextParameterName] =
Matches[CurrentMatch].Groups[GroupName].Value;
e.IsMet = true;
CurrentMatch++;
return;
}
e.IsMet = false;
}
public override void Initialize(object sender, ConditionalEventArgs e)
{
CurrentMatch = 0;
Matches = System.Text.RegularExpressions.Regex.Matches(
e.WebTest.LastResponse.BodyString,
Regex,
RegexOptions.IgnoreCase);
}
public override string StringRepresentation()
{
return "Regex condition " + Regex;
}
}
}