Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

renamed a few things, cleaned up

  • Loading branch information...
commit 681858c2d120f68e1ad480fd278485bb7e33109c 1 parent eb7e579
SubSonic Project subsonic authored
BIN  .DS_Store
View
Binary file not shown
20 LICENSE.txt
View
@@ -1,11 +1,11 @@
-New BSD License
-http://www.opensource.org/licenses/bsd-license.php
-Copyright (c) 2009, Rob Conery (robconery@gmail.com)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-Neither the name of the SubSonic nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+New BSD License
+http://www.opensource.org/licenses/bsd-license.php
+Copyright (c) 2009, Rob Conery (robconery@gmail.com)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of the SubSonic nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 Quixote.sln
View
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Quixote", "Quixote\Quixote.csproj", "{C2C23DB8-3D20-45C9-8496-86299CE3007C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C2C23DB8-3D20-45C9-8496-86299CE3007C}.Debug|x86.ActiveCfg = Debug|x86
+ {C2C23DB8-3D20-45C9-8496-86299CE3007C}.Debug|x86.Build.0 = Debug|x86
+ {C2C23DB8-3D20-45C9-8496-86299CE3007C}.Release|x86.ActiveCfg = Release|x86
+ {C2C23DB8-3D20-45C9-8496-86299CE3007C}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = Quixote\Quixote.csproj
+ EndGlobalSection
+EndGlobal
164 Examples/Basics.cshtml → Quixote/Examples/Basics.cshtml 100644 → 100755
View
@@ -1,83 +1,83 @@
-@using Quixote;
-
-@TheFollowing.Describes("Should assertions")
-
- @It.Should("True should be true", () => {
- return true.ShouldBeTrue();
- })
- @It.Should("False should be false", () => {
- return false.ShouldBeFalse();
- })
- @It.Should("2 should be greater than 1", () => {
- return 2.ShouldBeGreaterThan(1);
- })
- @It.Should("1 should be less than 2", () => {
- return 1.ShouldBeLessThan(2);
- })
- @It.Should("1 should equal 1", () => {
- return 1.ShouldEqual(1);
- })
- @It.Should("1 should not equal 2", () => {
- return 1.ShouldNotEqual(2);
- })
- @It.Should("DuckyDuck should contain ck", () => {
- return "DuckyDuck".ShouldContain("ck");
- })
- @It.Should("DuckyDuck should not contain fluffy", () => {
- return "DuckyDuck".ShouldNotContain("fluffy");
- })
- @It.Should("1...3 array should contain 2", () => {
- return new object[] { 1, 2, 3 }.ShouldContainItem(2);
- })
- @It.Should("1...3 array should not contain 47", () => {
- return new object[] { 1, 2, 3 }.ShouldNotContainItem(47);
- })
- @It.Should("1...3 list should contain 2", () => {
- var list = new List<int>() { 1, 2, 3 };
- return list.ShouldContainItem(2);
- })
- @It.Should("1...3 list should not contain 47", () => {
- var list = new List<int>() { 1, 2, 3 };
- return list.ShouldNotContainItem(47);
- })
- @It.Should("know how to count", () => {
- var list = new List<int>() { 1, 2, 3 };
- return list.ShouldHaveACountOf(3);
- })
- @It.Should("assert throws", () => {
- return Assert.ShouldThrow(() => { throw new Exception("hi there"); });
- })
-
- @It.Should("assert not throws", () => {
- return Assert.ShouldNotThrow(() => {var x = 1; });
- })
-
-
-
-@TheFollowing.Describes("Get Requests")
-
- @They.Should("Have a title that says welcome to our site", () => {
- return Runner.Get("~/").Title.ShouldContain("welcome to our site");
- })
- @They.Should("Have a 6 links in the body of the home page", () => {
- return Runner.Get("~/").BodyLinks.ShouldHaveACountOf(6);
-
- })
- @They.Should("Have a 2 headings in the body of the home page", () => {
- return Runner.Get("~/").Headings.ShouldHaveACountOf(2);
-
- })
- @They.Should("The second header should say It's a lot of fun", () => {
- return Runner.Get("~/").Headings[1].Trim().ShouldEqual("It's a lot of fun");
-
- })
-
-@TheFollowing.Describes("Post Requests")
- @They.Should("Post 2 values to the post page", () => {
- var result = Runner.Post("~/posty", new { thing1 = "stuff", thing2 = "morestuff" });
- return result.Html.Trim().ShouldEqual("POST - 2");
- })
- @They.Should("Record 'chocolate' as a form value", () => {
- var result = Runner.Post("~/posty", new { chocolate = "stuff", dookey = "morestuff" });
- return result.FormValues.ShouldContainKey("chocolate");
+@using Quixote;
+
+@TheFollowing.Describes("Should assertions")
+
+ @It.Should("True should be true", () => {
+ return true.ShouldBeTrue();
+ })
+ @It.Should("False should be false", () => {
+ return false.ShouldBeFalse();
+ })
+ @It.Should("2 should be greater than 1", () => {
+ return 2.ShouldBeGreaterThan(1);
+ })
+ @It.Should("1 should be less than 2", () => {
+ return 1.ShouldBeLessThan(2);
+ })
+ @It.Should("1 should equal 1", () => {
+ return 1.ShouldEqual(1);
+ })
+ @It.Should("1 should not equal 2", () => {
+ return 1.ShouldNotEqual(2);
+ })
+ @It.Should("DuckyDuck should contain ck", () => {
+ return "DuckyDuck".ShouldContain("ck");
+ })
+ @It.Should("DuckyDuck should not contain fluffy", () => {
+ return "DuckyDuck".ShouldNotContain("fluffy");
+ })
+ @It.Should("1...3 array should contain 2", () => {
+ return new object[] { 1, 2, 3 }.ShouldContainItem(2);
+ })
+ @It.Should("1...3 array should not contain 47", () => {
+ return new object[] { 1, 2, 3 }.ShouldNotContainItem(47);
+ })
+ @It.Should("1...3 list should contain 2", () => {
+ var list = new List<int>() { 1, 2, 3 };
+ return list.ShouldContainItem(2);
+ })
+ @It.Should("1...3 list should not contain 47", () => {
+ var list = new List<int>() { 1, 2, 3 };
+ return list.ShouldNotContainItem(47);
+ })
+ @It.Should("know how to count", () => {
+ var list = new List<int>() { 1, 2, 3 };
+ return list.ShouldHaveACountOf(3);
+ })
+ @It.Should("assert throws", () => {
+ return Assert.ShouldThrow(() => { throw new Exception("hi there"); });
+ })
+
+ @It.Should("assert not throws", () => {
+ return Assert.ShouldNotThrow(() => {var x = 1; });
+ })
+
+
+
+@TheFollowing.Describes("Get Requests")
+
+ @They.Should("Have a title that says welcome to our site", () => {
+ return Runner.Get("~/").Title.ShouldContain("welcome to our site");
+ })
+ @They.Should("Have a 6 links in the body of the home page", () => {
+ return Runner.Get("~/").BodyLinks.ShouldHaveACountOf(6);
+
+ })
+ @They.Should("Have a 2 headings in the body of the home page", () => {
+ return Runner.Get("~/").Headings.ShouldHaveACountOf(2);
+
+ })
+ @They.Should("The second header should say It's a lot of fun", () => {
+ return Runner.Get("~/").Headings[1].Trim().ShouldEqual("It's a lot of fun");
+
+ })
+
+@TheFollowing.Describes("Post Requests")
+ @They.Should("Post 2 values to the post page", () => {
+ var result = Runner.Post("~/posty", new { thing1 = "stuff", thing2 = "morestuff" });
+ return result.Html.Trim().ShouldEqual("POST - 2");
+ })
+ @They.Should("Record 'chocolate' as a form value", () => {
+ var result = Runner.Post("~/posty", new { chocolate = "stuff", dookey = "morestuff" });
+ return result.FormValues.ShouldContainKey("chocolate");
})
5 Quixote.cs → Quixote/Quixote.cs 100644 → 100755
View
@@ -8,7 +8,10 @@
using System.Dynamic;
using System.Collections;
using System.Text;
-namespace Quixote{
+
+//you can include this in your WebMatrix app and run from there
+//instead of the console runner
+namespace QuixoteWeb{
public static class Runner {
/// <summary>
/// This is a helper method that will decipher the root URL of your site
48 Quixote/Quixote.csproj
View
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{C2C23DB8-3D20-45C9-8496-86299CE3007C}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <RootNamespace>Quixote</RootNamespace>
+ <AssemblyName>Quixote</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <DebugType>none</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <Compile Include="Quixote.cs" />
+ <Compile Include="QuixoteConsole.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Quixote.css" />
+ <None Include="Examples\Basics.cshtml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="Mono.CSharp" />
+ <Reference Include="System.Net" />
+ <Reference Include="System.Web" />
+ </ItemGroup>
+</Project>
54 Quixote.css → Quixote/Quixote.css 100644 → 100755
View
@@ -1,28 +1,28 @@
-.result
-{
- margin-bottom:6px;
- width:600px;
- margin-left:24px;
- font-size:.8em;
-}
-
-
-.fail,
-.pending,
-.pass { padding:4px; border: 2px solid #ddd; }
-.fail { background: #FBE3E4; color: #D12F19; border-color: #FBC2C4}
-.pending { background: #FFF6BF; color: #817134; border-color: #FFD324; }
-.pass { background: #E6EFC2; color: #529214; border-color: #C6D880; }
-
-.exception
-{
- background-color:#ffffcc;
- padding:22px;
-}
-
-textarea.exception-box
-{
- border:2px solid #ccc;
- height:90px;
- width:90%;
+.result
+{
+ margin-bottom:6px;
+ width:600px;
+ margin-left:24px;
+ font-size:.8em;
+}
+
+
+.fail,
+.pending,
+.pass { padding:4px; border: 2px solid #ddd; }
+.fail { background: #FBE3E4; color: #D12F19; border-color: #FBC2C4}
+.pending { background: #FFF6BF; color: #817134; border-color: #FFD324; }
+.pass { background: #E6EFC2; color: #529214; border-color: #C6D880; }
+
+.exception
+{
+ background-color:#ffffcc;
+ padding:22px;
+}
+
+textarea.exception-box
+{
+ border:2px solid #ccc;
+ height:90px;
+ width:90%;
}
8 Quixote/Quixote.userprefs
View
@@ -0,0 +1,8 @@
+<Properties>
+ <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug|x86" />
+ <MonoDevelop.Ide.Workbench />
+ <MonoDevelop.Ide.DebuggingService.Breakpoints>
+ <BreakpointStore />
+ </MonoDevelop.Ide.DebuggingService.Breakpoints>
+ <MonoDevelop.Ide.DebuggingService.PinnedWatches />
+</Properties>
1,128 QuixoteConsole.cs → Quixote/QuixoteConsole.cs 100644 → 100755
View
@@ -1,565 +1,565 @@
-using System;
-using System.Collections.Generic;
-using System.Text.RegularExpressions;
-using System.Web;
-using System.Net;
-using System.IO;
-using System.Linq;
-using System.Dynamic;
-using System.Collections;
-using System.Text;
-namespace Quixote {
- public static class Runner {
-
- public static string SiteRoot { get; set; }
- /// <summary>
- /// A bit verbose, but since we're using dynamics you can't attach an extension method - which is sad.
- /// This includes core system LINQ stuff, like "FirstOrDefault".
- /// </summary>
- static string FindMatch(string source, string find) {
- Regex reg = new Regex(find, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);
- return reg.Match(source).Value;
-
- }
- /// <summary>
- /// Matching core for loading up stuff on a web page
- /// </summary>
- static List<string> FindMatches(string source, string find) {
- Regex reg = new Regex(find, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);
-
- List<string> result = new List<string>();
-
- foreach (Match m in reg.Matches(source))
- result.Add(m.Value);
- return result;
- }
-
- /// <summary>
- /// This method parses the Get request into digestable little chunks so you can query the response's title, links,
- /// headings, body, body links, etc.
- /// </summary>
- static void ParseResult(dynamic result) {
- string bodyPattern = "(?<=<body>).*?(?=</body>)";
- string titlePattern = "(?<=<title>).*?(?=</title>)";
- string linkPattern = "<a.*?>.*?</a>";
- string headPattern = @"(?<=<h\d\w*?>).*?(?=</h\d>)";
-
- //have to cast this ToString() as result.Html is a dynamic (which is actually an HtmlString)
- var html = result.Html.ToString();
-
- result.Title = FindMatch(html, titlePattern);
- result.Links = FindMatches(html, linkPattern);
- result.Headings = FindMatches(html, headPattern);
- result.Body = FindMatch(html, bodyPattern);
- result.BodyLinks = FindMatches(result.Body.ToString(), linkPattern);
- }
-
- /// <summary>
- /// Resets the virtual URL to an absolute so the web request knows what to do
- /// Won't accept URLs that aren't virtual
- /// </summary>
- static string DecipherLocalUrl(string url) {
- //this URL needs to be local
- if (url.Contains("http")) {
- throw new InvalidOperationException("Get() only supports local URLs. You can use absolute or relative");
- }
-
- //convert it...
- var relUrl = VirtualPathUtility.ToAbsolute(url);
- if(relUrl.StartsWith("/"))
- relUrl = relUrl.TrimStart('/');
-
- if (!SiteRoot.EndsWith("/"))
- SiteRoot += "/";
- var absUrl = SiteRoot + relUrl;
- return absUrl;
-
- }
- /// <summary>
- /// Reads the Response and pops the result into a WebResponse
- /// </summary>
- static WebResponse ReadAndLoadResponse(HttpWebRequest request, Cookie cookie) {
- var result = new WebResponse();
- //add the cookie if needed
- if (cookie != null) {
- request.CookieContainer.Add(cookie);
- }
- // Execute the query
- var response = (HttpWebResponse)request.GetResponse();
- result.Response = response;
- result.Url = result.Response.ResponseUri.AbsoluteUri;
- result.Code = result.Response.StatusCode;
-
- using (StreamReader sr = new StreamReader(response.GetResponseStream())) {
- result.Html = sr.ReadToEnd();
- sr.Close();
- }
- ParseResult(result);
- return result;
-
- }
-
- /// <summary>
- /// Sends a POST request to your app, with the postData being an Anonymous object
- /// </summary>
- public static WebResponse Post(string url, object postData, Cookie cookie = null) {
-
- //this URL needs to be local
- var absUrl = DecipherLocalUrl(url);
-
- //deserialize the form values
- var list = new List<string>();
- var postParams = "";
- //this will be used later on
- var formValues = new Dictionary<string, object>();
-
- if (postData != null) {
- var props = postData.GetType().GetProperties();
- var d = (IDictionary)formValues;
- foreach (var prop in props) {
- var key = prop.Name;
- var val = prop.GetValue(postData, null);
- d.Add(key, val);
- list.Add(string.Format("{0}={1}", key, HttpUtility.UrlEncode(val.ToString())));
- }
- postParams = String.Join("&", list.ToArray());
- }
-
- // Set the encoding type
- HttpWebRequest request = (HttpWebRequest)WebRequest.Create(absUrl);
-
- request.Method = "POST";
- request.ContentType = "application/x-www-form-urlencoded";
-
- // Build a string containing all the parameters
- request.ContentLength = postParams.Length;
-
- // We write the parameters into the request
- using (StreamWriter sw = new StreamWriter(request.GetRequestStream())) {
- sw.Write(postParams);
- sw.Close();
- }
- var result = ReadAndLoadResponse(request, cookie);
- result.FormValues = formValues;
- return result;
- }
-
- /// <summary>
- /// Will run a GET request on your site
- /// </summary>
- public static WebResponse Get(string url, Cookie cookie = null) {
- var absUrl = DecipherLocalUrl(url);
- var result = new WebResponse();
-
- HttpWebRequest request = (HttpWebRequest)WebRequest.Create(absUrl);
-
- return ReadAndLoadResponse(request, cookie);
- }
-
- }
-
- public static class TheFollowing {
- /// <summary>
- /// Helps readability if you describe the context of your tests
- /// </summary>
- public static void Describes(string context) {
- Console.WriteLine("---------------------------------------");
- Console.WriteLine(" {0} ", context);
- Console.WriteLine("---------------------------------------");
- }
- }
- /// <summary>
- /// Syntactic Sugar
- /// </summary>
- public static class They {
- public static void Should(string name, Func<dynamic> action) {
- It.Should(name, action);
- }
- public static void ShouldNot(string name, Func<dynamic> action) {
- It.Should(name, action);
- }
- public static void Should(string name) {
- It.Should(name);
- }
- public static void ShouldNot(string name) {
- It.Should(name);
- }
- }
- /// <summary>
- /// The core execution class of Quixote
- /// </summary>
- public static class It {
-
- /// <summary>
- /// Executes the test call
- /// </summary>
- public static dynamic ExecuteIt(Func<dynamic> action) {
- dynamic result = new System.Dynamic.ExpandoObject();
- try {
- result = action.Invoke();
- } catch (Exception x) {
- result.Passed = false;
- result.Result = new HtmlString(BuildExceptionMessage(x).Trim());
- result.Message = x.Message;
- result.Threw = true;
- }
-
- return result;
- }
-
- public static string BuildExceptionMessage(Exception x) {
-
- Exception logException = x;
- if (x.InnerException != null)
- logException = x.InnerException;
- var sb = new StringBuilder();
-
-
- // Get the error message
- sb.AppendLine("Message :" + logException.Message);
-
- // Source of the message
- sb.AppendLine("Source :" + logException.Source);
-
- // Stack Trace of the error
-
- sb.AppendLine("Stack Trace :" + logException.StackTrace);
-
- // Method where the error occurred
- sb.AppendLine("TargetSite :" + logException.TargetSite);
- return sb.ToString();
- }
-
-
- /// <summary>
- /// There is no difference between Should/ShouldNot. Just readability
- /// </summary>
- public static void ShouldNot(string name) {
- Should(name);
- }
- public static void ShouldNot(string name, Func<dynamic> action) {
- Should(name, action);
- }
- /// <summary>
- /// Stubs out a test and marks it as "pending"
- /// </summary>
- public static void Should(string name) {
- Console.WriteLine("** should {0}: PENDING", name);
- }
- /// <summary>
- /// A method that prepares a test for execution
- /// </summary>
- public static void Should(string name, Func<dynamic> action) {
- var test = ExecuteIt(action);
- if (test.Threw) {
- Console.WriteLine("** Should {0} - FAIL (Exception thrown: {1})", name, test.Message);
- Console.WriteLine(test.Result);
- } else {
- if (test.Passed) {
- Console.ForegroundColor = ConsoleColor.Green;
- Console.WriteLine("** Should {0} -- PASSED", name);
- Console.ResetColor();
- } else {
- Console.ForegroundColor = ConsoleColor.Red;
- Console.WriteLine("** Should {0} -- FAIL", name);
- Console.WriteLine(" -- {0}", test.Message);
- Console.ResetColor();
- }
- }
- }
- }
-
- /// <summary>
- /// Wish I could use dynamic for this - but I need to have a higher order of logic here, and I also need to be able
- /// to attach extension methods to it for the assertions. Dynamics can't have extension methods :( and I want you to
- /// be able to run "response.Title.ShouldContain("Sex Appeal");
- /// </summary>
- public class WebResponse {
-
- public string Body { get; set; }
- public HttpStatusCode Code { get; set; }
- public string Url { get; set; }
- public HttpWebResponse Response { get; set; }
- public HttpWebRequest Request { get; set; }
- public string Html { get; set; }
- //Html Stuff
- public string Title { get; set; }
- public List<string> Links { get; set; }
- public List<string> BodyLinks { get; set; }
- public List<string> Headings { get; set; }
- public Dictionary<string, object> FormValues { get; set; }
- }
-
- /// <summary>
- /// This is the assertion library. These are all Extension methods except for ShouldThrow and ShouldNotThrow
- /// Add to it as you will :)
- /// </summary>
- public static class Assert {
-
- static dynamic InitResult() {
- dynamic result = new ExpandoObject();
- result.Passed = "false";
- result.Threw = false;
- result.Message = "";
- return result;
- }
-
- public static dynamic ShouldBeTrue(this bool condition) {
-
- dynamic result = InitResult();
- result.Passed = condition;
- return result;
- }
- public static dynamic ShouldBeFalse(this bool condition) {
-
- dynamic result = InitResult();
- result.Passed = !condition;
- return result;
- }
- public static dynamic ShouldBeGreaterThan(this int o, int item) {
-
- dynamic result = InitResult();
- result.Passed = o > item;
- if (!result.Passed) {
- result.Message = o.ToString() + " is less than " + item.ToString();
- }
-
- return result;
- }
- public static dynamic ShouldBeGreaterThan(this decimal o, decimal item) {
-
- dynamic result = InitResult();
- result.Passed = o > item;
- if (!result.Passed) {
- result.Message = o.ToString() + " is less than " + item.ToString();
- }
-
- return result;
- }
- public static dynamic ShouldBeGreaterThan(this double o, double item) {
-
- dynamic result = InitResult();
- result.Passed = o > item;
- if (!result.Passed) {
- result.Message = o.ToString() + " is less than " + item.ToString();
- }
-
- return result;
- }
- public static dynamic ShouldBeGreaterThan(this float o, float item) {
-
- dynamic result = InitResult();
- result.Passed = o > item;
- if (!result.Passed) {
- result.Message = o.ToString() + " is less than " + item.ToString();
- }
-
- return result;
- }
-
-
- public static dynamic ShouldBeLessThan(this int o, int item) {
-
- dynamic result = InitResult();
- result.Passed = o < item;
- if (!result.Passed) {
- result.Message = o.ToString() + " is greater than " + item.ToString();
- }
-
- return result;
- }
- public static dynamic ShouldBeLessThan(this decimal o, decimal item) {
-
- dynamic result = InitResult();
- result.Passed = o < item;
- if (!result.Passed) {
- result.Message = o.ToString() + " is greater than " + item.ToString();
- }
- return result;
- }
- public static dynamic ShouldBeLessThan(this double o, double item) {
-
- dynamic result = InitResult();
- result.Passed = o < item;
- if (!result.Passed) {
- result.Message = o.ToString() + " is greater than " + item.ToString();
- }
- return result;
- }
- public static dynamic ShouldBeLessThan(this float o, float item) {
-
- dynamic result = InitResult();
- result.Passed = o < item;
- if (!result.Passed) {
- result.Message = o.ToString() + " is greater than " + item.ToString();
- }
- return result;
- }
-
-
- public static dynamic ShouldBeNull(this object item) {
- dynamic result = InitResult();
-
- result.Passed = item == null;
- if (!result.Passed) {
- result.Message = item.ToString() + " isn't null...";
- }
- return result;
- }
-
- public static dynamic ShouldNotBeNull(this object item) {
- dynamic result = InitResult();
-
- result.Passed = item != null;
- if (!result.Passed) {
- result.Message = "Nope - it's null...";
- }
-
- return result;
- }
-
- public static dynamic ShouldThrow(Action a) {
- dynamic result = InitResult();
-
- try {
- a.Invoke();
- result.Passed = false;
- result.Message = "Didnt' throw";
- } catch {
- result.Passed = true;
- }
-
- return result;
- }
- public static dynamic ShouldNotThrow(Action a) {
- dynamic result = InitResult();
-
- try {
- a.Invoke();
- result.Passed = true;
- } catch (Exception x) {
- result.Passed = false;
- result.Message = "BOOM - threw a " + x.GetType().Name;
- }
-
- return result;
- }
- public static dynamic ShouldEqual(this object first, object second) {
- dynamic result = InitResult();
- result.Passed = first.Equals(second);
- if (!result.Passed) {
- result.Message = "Expected " + first.ToString() + " but was " + second.ToString();
- }
-
- return result;
- }
-
- public static dynamic ShouldNotEqual(this object first, object second) {
- dynamic result = InitResult();
- result.Passed = !first.Equals(second);
- if (!result.Passed) {
- result.Message = first.ToString() + " is equal to " + second.ToString();
- }
-
- return result;
- }
- public static dynamic ShouldContain(this string content, string item) {
- dynamic result = InitResult();
- result.Passed = content.Contains(item);
- if (!result.Passed) {
- result.Message = string.Format("'{0}' not found in sample: {1}", item, content);
- }
- return result;
- }
- public static dynamic ShouldNotContain(this string content, string item) {
- dynamic result = InitResult();
- result.Passed = !content.Contains(item);
- if (!result.Passed) {
- result.Message = string.Format("'{0}' found in sample", item);
- }
- return result;
- }
- public static dynamic ShouldContainItem<T>(this IEnumerable<T> list, object item) {
- dynamic result = InitResult();
- result.Passed = list.Any(x => x.Equals(item));
- if (!result.Passed) {
- result.Message = string.Format("'{0}' not found in list", item);
- }
- return result;
- }
- public static dynamic ShouldContainItem<T>(this T[] list, object item) {
- dynamic result = InitResult();
- result.Passed = list.Any(x => x.Equals(item));
- if (!result.Passed) {
- result.Message = string.Format("'{0}' not found in list", item);
- }
- return result;
- }
- public static dynamic ShouldNotContainItem<T>(this IEnumerable<T> list, object item) {
- dynamic result = InitResult();
- result.Passed = !list.Any(x => x.Equals(item));
- if (!result.Passed) {
- result.Message = string.Format("'{0}' found in list", item);
- }
- return result;
- }
-
- public static dynamic ShouldContainString<T>(this IEnumerable<T> list, string item) {
- dynamic result = InitResult();
- result.Passed = list.Any(x => x.ToString().Contains(item));
- if (!result.Passed) {
- result.Message = string.Format("'{0}' not found in list", item);
- }
- return result;
- }
- public static dynamic ShouldNotContainString<T>(this IEnumerable<T> list, string item) {
- dynamic result = InitResult();
- result.Passed = !list.Any(x => x.ToString().Contains(item));
- if (!result.Passed) {
- result.Message = string.Format("'{0}' found in list", item);
- }
- return result;
- }
- public static dynamic ShouldContainKey<TKey, TVal>(this IDictionary<TKey, TVal> list, TKey key) {
- dynamic result = InitResult();
- result.Passed = list.ContainsKey(key);
- if (!result.Passed) {
- result.Message = string.Format("'{0}' not found in keys", key);
- }
- return result;
- }
- public static dynamic ShouldNotContainKey<TKey, TVal>(this IDictionary<TKey, TVal> list, TKey key) {
- dynamic result = InitResult();
- result.Passed = !list.ContainsKey(key);
- if (!result.Passed) {
- result.Message = string.Format("'{0}' found in keys", key);
- }
- return result;
- }
- public static dynamic ShouldContainValue<TKey, TVal>(this IDictionary<TKey, TVal> list, TVal val) {
- dynamic result = InitResult();
- result.Passed = list.Values.Contains(val);
- if (!result.Passed) {
- result.Message = string.Format("'{0}' not found in values", val);
- }
- return result;
- }
- public static dynamic ShouldNotContainValue<TKey, TVal>(this IDictionary<TKey, TVal> list, TVal val) {
- dynamic result = InitResult();
- result.Passed = !list.Values.Contains(val);
- if (!result.Passed) {
- result.Message = string.Format("'{0}' found in values", val);
- }
- return result;
- }
- public static dynamic ShouldHaveACountOf<T>(this IEnumerable<T> list, int count) {
- dynamic result = InitResult();
- result.Passed = list.Count() == count;
- if (!result.Passed) {
- result.Message = string.Format("Expected {0} but was {1}", count, list.Count());
- }
- return result;
- }
-
- }
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Web;
+using System.Net;
+using System.IO;
+using System.Linq;
+using System.Dynamic;
+using System.Collections;
+using System.Text;
+namespace Quixote {
+ public static class Runner {
+
+ public static string SiteRoot { get; set; }
+ /// <summary>
+ /// A bit verbose, but since we're using dynamics you can't attach an extension method - which is sad.
+ /// This includes core system LINQ stuff, like "FirstOrDefault".
+ /// </summary>
+ static string FindMatch(string source, string find) {
+ Regex reg = new Regex(find, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);
+ return reg.Match(source).Value;
+
+ }
+ /// <summary>
+ /// Matching core for loading up stuff on a web page
+ /// </summary>
+ static List<string> FindMatches(string source, string find) {
+ Regex reg = new Regex(find, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline);
+
+ List<string> result = new List<string>();
+
+ foreach (Match m in reg.Matches(source))
+ result.Add(m.Value);
+ return result;
+ }
+
+ /// <summary>
+ /// This method parses the Get request into digestable little chunks so you can query the response's title, links,
+ /// headings, body, body links, etc.
+ /// </summary>
+ static void ParseResult(dynamic result) {
+ string bodyPattern = "(?<=<body>).*?(?=</body>)";
+ string titlePattern = "(?<=<title>).*?(?=</title>)";
+ string linkPattern = "<a.*?>.*?</a>";
+ string headPattern = @"(?<=<h\d\w*?>).*?(?=</h\d>)";
+
+ //have to cast this ToString() as result.Html is a dynamic (which is actually an HtmlString)
+ var html = result.Html.ToString();
+
+ result.Title = FindMatch(html, titlePattern);
+ result.Links = FindMatches(html, linkPattern);
+ result.Headings = FindMatches(html, headPattern);
+ result.Body = FindMatch(html, bodyPattern);
+ result.BodyLinks = FindMatches(result.Body.ToString(), linkPattern);
+ }
+
+ /// <summary>
+ /// Resets the virtual URL to an absolute so the web request knows what to do
+ /// Won't accept URLs that aren't virtual
+ /// </summary>
+ static string DecipherLocalUrl(string url) {
+ //this URL needs to be local
+ if (url.Contains("http")) {
+ throw new InvalidOperationException("Get() only supports local URLs. You can use absolute or relative");
+ }
+
+ //convert it...
+ var relUrl = VirtualPathUtility.ToAbsolute(url);
+ if(relUrl.StartsWith("/"))
+ relUrl = relUrl.TrimStart('/');
+
+ if (!SiteRoot.EndsWith("/"))
+ SiteRoot += "/";
+ var absUrl = SiteRoot + relUrl;
+ return absUrl;
+
+ }
+ /// <summary>
+ /// Reads the Response and pops the result into a WebResponse
+ /// </summary>
+ static WebResponse ReadAndLoadResponse(HttpWebRequest request, Cookie cookie) {
+ var result = new WebResponse();
+ //add the cookie if needed
+ if (cookie != null) {
+ request.CookieContainer.Add(cookie);
+ }
+ // Execute the query
+ var response = (HttpWebResponse)request.GetResponse();
+ result.Response = response;
+ result.Url = result.Response.ResponseUri.AbsoluteUri;
+ result.Code = result.Response.StatusCode;
+
+ using (StreamReader sr = new StreamReader(response.GetResponseStream())) {
+ result.Html = sr.ReadToEnd();
+ sr.Close();
+ }
+ ParseResult(result);
+ return result;
+
+ }
+
+ /// <summary>
+ /// Sends a POST request to your app, with the postData being an Anonymous object
+ /// </summary>
+ public static WebResponse Post(string url, object postData, Cookie cookie = null) {
+
+ //this URL needs to be local
+ var absUrl = DecipherLocalUrl(url);
+
+ //deserialize the form values
+ var list = new List<string>();
+ var postParams = "";
+ //this will be used later on
+ var formValues = new Dictionary<string, object>();
+
+ if (postData != null) {
+ var props = postData.GetType().GetProperties();
+ var d = (IDictionary)formValues;
+ foreach (var prop in props) {
+ var key = prop.Name;
+ var val = prop.GetValue(postData, null);
+ d.Add(key, val);
+ list.Add(string.Format("{0}={1}", key, HttpUtility.UrlEncode(val.ToString())));
+ }
+ postParams = String.Join("&", list.ToArray());
+ }
+
+ // Set the encoding type
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(absUrl);
+
+ request.Method = "POST";
+ request.ContentType = "application/x-www-form-urlencoded";
+
+ // Build a string containing all the parameters
+ request.ContentLength = postParams.Length;
+
+ // We write the parameters into the request
+ using (StreamWriter sw = new StreamWriter(request.GetRequestStream())) {
+ sw.Write(postParams);
+ sw.Close();
+ }
+ var result = ReadAndLoadResponse(request, cookie);
+ result.FormValues = formValues;
+ return result;
+ }
+
+ /// <summary>
+ /// Will run a GET request on your site
+ /// </summary>
+ public static WebResponse Get(string url, Cookie cookie = null) {
+ var absUrl = DecipherLocalUrl(url);
+ var result = new WebResponse();
+
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(absUrl);
+
+ return ReadAndLoadResponse(request, cookie);
+ }
+
+ }
+
+ public static class TheFollowing {
+ /// <summary>
+ /// Helps readability if you describe the context of your tests
+ /// </summary>
+ public static void Describes(string context) {
+ Console.WriteLine("---------------------------------------");
+ Console.WriteLine(" {0} ", context);
+ Console.WriteLine("---------------------------------------");
+ }
+ }
+ /// <summary>
+ /// Syntactic Sugar
+ /// </summary>
+ public static class They {
+ public static void Should(string name, Func<dynamic> action) {
+ It.Should(name, action);
+ }
+ public static void ShouldNot(string name, Func<dynamic> action) {
+ It.Should(name, action);
+ }
+ public static void Should(string name) {
+ It.Should(name);
+ }
+ public static void ShouldNot(string name) {
+ It.Should(name);
+ }
+ }
+ /// <summary>
+ /// The core execution class of Quixote
+ /// </summary>
+ public static class It {
+
+ /// <summary>
+ /// Executes the test call
+ /// </summary>
+ public static dynamic ExecuteIt(Func<dynamic> action) {
+ dynamic result = new System.Dynamic.ExpandoObject();
+ try {
+ result = action.Invoke();
+ } catch (Exception x) {
+ result.Passed = false;
+ result.Result = new HtmlString(BuildExceptionMessage(x).Trim());
+ result.Message = x.Message;
+ result.Threw = true;
+ }
+
+ return result;
+ }
+
+ public static string BuildExceptionMessage(Exception x) {
+
+ Exception logException = x;
+ if (x.InnerException != null)
+ logException = x.InnerException;
+ var sb = new StringBuilder();
+
+
+ // Get the error message
+ sb.AppendLine("Message :" + logException.Message);
+
+ // Source of the message
+ sb.AppendLine("Source :" + logException.Source);
+
+ // Stack Trace of the error
+
+ sb.AppendLine("Stack Trace :" + logException.StackTrace);
+
+ // Method where the error occurred
+ sb.AppendLine("TargetSite :" + logException.TargetSite);
+ return sb.ToString();
+ }
+
+
+ /// <summary>
+ /// There is no difference between Should/ShouldNot. Just readability
+ /// </summary>
+ public static void ShouldNot(string name) {
+ Should(name);
+ }
+ public static void ShouldNot(string name, Func<dynamic> action) {
+ Should(name, action);
+ }
+ /// <summary>
+ /// Stubs out a test and marks it as "pending"
+ /// </summary>
+ public static void Should(string name) {
+ Console.WriteLine("** should {0}: PENDING", name);
+ }
+ /// <summary>
+ /// A method that prepares a test for execution
+ /// </summary>
+ public static void Should(string name, Func<dynamic> action) {
+ var test = ExecuteIt(action);
+ if (test.Threw) {
+ Console.WriteLine("** Should {0} - FAIL (Exception thrown: {1})", name, test.Message);
+ Console.WriteLine(test.Result);
+ } else {
+ if (test.Passed) {
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine("** Should {0} -- PASSED", name);
+ Console.ResetColor();
+ } else {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("** Should {0} -- FAIL", name);
+ Console.WriteLine(" -- {0}", test.Message);
+ Console.ResetColor();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Wish I could use dynamic for this - but I need to have a higher order of logic here, and I also need to be able
+ /// to attach extension methods to it for the assertions. Dynamics can't have extension methods :( and I want you to
+ /// be able to run "response.Title.ShouldContain("Sex Appeal");
+ /// </summary>
+ public class WebResponse {
+
+ public string Body { get; set; }
+ public HttpStatusCode Code { get; set; }
+ public string Url { get; set; }
+ public HttpWebResponse Response { get; set; }
+ public HttpWebRequest Request { get; set; }
+ public string Html { get; set; }
+ //Html Stuff
+ public string Title { get; set; }
+ public List<string> Links { get; set; }
+ public List<string> BodyLinks { get; set; }
+ public List<string> Headings { get; set; }
+ public Dictionary<string, object> FormValues { get; set; }
+ }
+
+ /// <summary>
+ /// This is the assertion library. These are all Extension methods except for ShouldThrow and ShouldNotThrow
+ /// Add to it as you will :)
+ /// </summary>
+ public static class Assert {
+
+ static dynamic InitResult() {
+ dynamic result = new ExpandoObject();
+ result.Passed = "false";
+ result.Threw = false;
+ result.Message = "";
+ return result;
+ }
+
+ public static dynamic ShouldBeTrue(this bool condition) {
+
+ dynamic result = InitResult();
+ result.Passed = condition;
+ return result;
+ }
+ public static dynamic ShouldBeFalse(this bool condition) {
+
+ dynamic result = InitResult();
+ result.Passed = !condition;
+ return result;
+ }
+ public static dynamic ShouldBeGreaterThan(this int o, int item) {
+
+ dynamic result = InitResult();
+ result.Passed = o > item;
+ if (!result.Passed) {
+ result.Message = o.ToString() + " is less than " + item.ToString();
+ }
+
+ return result;
+ }
+ public static dynamic ShouldBeGreaterThan(this decimal o, decimal item) {
+
+ dynamic result = InitResult();
+ result.Passed = o > item;
+ if (!result.Passed) {
+ result.Message = o.ToString() + " is less than " + item.ToString();
+ }
+
+ return result;
+ }
+ public static dynamic ShouldBeGreaterThan(this double o, double item) {
+
+ dynamic result = InitResult();
+ result.Passed = o > item;
+ if (!result.Passed) {
+ result.Message = o.ToString() + " is less than " + item.ToString();
+ }
+
+ return result;
+ }
+ public static dynamic ShouldBeGreaterThan(this float o, float item) {
+
+ dynamic result = InitResult();
+ result.Passed = o > item;
+ if (!result.Passed) {
+ result.Message = o.ToString() + " is less than " + item.ToString();
+ }
+
+ return result;
+ }
+
+
+ public static dynamic ShouldBeLessThan(this int o, int item) {
+
+ dynamic result = InitResult();
+ result.Passed = o < item;
+ if (!result.Passed) {
+ result.Message = o.ToString() + " is greater than " + item.ToString();
+ }
+
+ return result;
+ }
+ public static dynamic ShouldBeLessThan(this decimal o, decimal item) {
+
+ dynamic result = InitResult();
+ result.Passed = o < item;
+ if (!result.Passed) {
+ result.Message = o.ToString() + " is greater than " + item.ToString();
+ }
+ return result;
+ }
+ public static dynamic ShouldBeLessThan(this double o, double item) {
+
+ dynamic result = InitResult();
+ result.Passed = o < item;
+ if (!result.Passed) {
+ result.Message = o.ToString() + " is greater than " + item.ToString();
+ }
+ return result;
+ }
+ public static dynamic ShouldBeLessThan(this float o, float item) {
+
+ dynamic result = InitResult();
+ result.Passed = o < item;
+ if (!result.Passed) {
+ result.Message = o.ToString() + " is greater than " + item.ToString();
+ }
+ return result;
+ }
+
+
+ public static dynamic ShouldBeNull(this object item) {
+ dynamic result = InitResult();
+
+ result.Passed = item == null;
+ if (!result.Passed) {
+ result.Message = item.ToString() + " isn't null...";
+ }
+ return result;
+ }
+
+ public static dynamic ShouldNotBeNull(this object item) {
+ dynamic result = InitResult();
+
+ result.Passed = item != null;
+ if (!result.Passed) {
+ result.Message = "Nope - it's null...";
+ }
+
+ return result;
+ }
+
+ public static dynamic ShouldThrow(Action a) {
+ dynamic result = InitResult();
+
+ try {
+ a.Invoke();
+ result.Passed = false;
+ result.Message = "Didnt' throw";
+ } catch {
+ result.Passed = true;
+ }
+
+ return result;
+ }
+ public static dynamic ShouldNotThrow(Action a) {
+ dynamic result = InitResult();
+
+ try {
+ a.Invoke();
+ result.Passed = true;
+ } catch (Exception x) {
+ result.Passed = false;
+ result.Message = "BOOM - threw a " + x.GetType().Name;
+ }
+
+ return result;
+ }
+ public static dynamic ShouldEqual(this object first, object second) {
+ dynamic result = InitResult();
+ result.Passed = first.Equals(second);
+ if (!result.Passed) {
+ result.Message = "Expected " + first.ToString() + " but was " + second.ToString();
+ }
+
+ return result;
+ }
+
+ public static dynamic ShouldNotEqual(this object first, object second) {
+ dynamic result = InitResult();
+ result.Passed = !first.Equals(second);
+ if (!result.Passed) {
+ result.Message = first.ToString() + " is equal to " + second.ToString();
+ }
+
+ return result;
+ }
+ public static dynamic ShouldContain(this string content, string item) {
+ dynamic result = InitResult();
+ result.Passed = content.Contains(item);
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' not found in sample: {1}", item, content);
+ }
+ return result;
+ }
+ public static dynamic ShouldNotContain(this string content, string item) {
+ dynamic result = InitResult();
+ result.Passed = !content.Contains(item);
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' found in sample", item);
+ }
+ return result;
+ }
+ public static dynamic ShouldContainItem<T>(this IEnumerable<T> list, object item) {
+ dynamic result = InitResult();
+ result.Passed = list.Any(x => x.Equals(item));
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' not found in list", item);
+ }
+ return result;
+ }
+ public static dynamic ShouldContainItem<T>(this T[] list, object item) {
+ dynamic result = InitResult();
+ result.Passed = list.Any(x => x.Equals(item));
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' not found in list", item);
+ }
+ return result;
+ }
+ public static dynamic ShouldNotContainItem<T>(this IEnumerable<T> list, object item) {
+ dynamic result = InitResult();
+ result.Passed = !list.Any(x => x.Equals(item));
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' found in list", item);
+ }
+ return result;
+ }
+
+ public static dynamic ShouldContainString<T>(this IEnumerable<T> list, string item) {
+ dynamic result = InitResult();
+ result.Passed = list.Any(x => x.ToString().Contains(item));
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' not found in list", item);
+ }
+ return result;
+ }
+ public static dynamic ShouldNotContainString<T>(this IEnumerable<T> list, string item) {
+ dynamic result = InitResult();
+ result.Passed = !list.Any(x => x.ToString().Contains(item));
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' found in list", item);
+ }
+ return result;
+ }
+ public static dynamic ShouldContainKey<TKey, TVal>(this IDictionary<TKey, TVal> list, TKey key) {
+ dynamic result = InitResult();
+ result.Passed = list.ContainsKey(key);
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' not found in keys", key);
+ }
+ return result;
+ }
+ public static dynamic ShouldNotContainKey<TKey, TVal>(this IDictionary<TKey, TVal> list, TKey key) {
+ dynamic result = InitResult();
+ result.Passed = !list.ContainsKey(key);
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' found in keys", key);
+ }
+ return result;
+ }
+ public static dynamic ShouldContainValue<TKey, TVal>(this IDictionary<TKey, TVal> list, TVal val) {
+ dynamic result = InitResult();
+ result.Passed = list.Values.Contains(val);
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' not found in values", val);
+ }
+ return result;
+ }
+ public static dynamic ShouldNotContainValue<TKey, TVal>(this IDictionary<TKey, TVal> list, TVal val) {
+ dynamic result = InitResult();
+ result.Passed = !list.Values.Contains(val);
+ if (!result.Passed) {
+ result.Message = string.Format("'{0}' found in values", val);
+ }
+ return result;
+ }
+ public static dynamic ShouldHaveACountOf<T>(this IEnumerable<T> list, int count) {
+ dynamic result = InitResult();
+ result.Passed = list.Count() == count;
+ if (!result.Passed) {
+ result.Message = string.Format("Expected {0} but was {1}", count, list.Count());
+ }
+ return result;
+ }
+
+ }
}
200 README.markdown
View
@@ -1,101 +1,101 @@
-Quixote is a single-file, drop-in high-level test-runner for ASP.NET/WebMatrix using .NET 4.0
-=================================================================================================
-
-If you know Rails, you know RSpec and Cucumber. These high-level testing frameworks allow you to test how your app responds when people use your application (GET and POST requests, etc). These frameworks are pretty advanced - Cucumber allows you to simulate button and link clicks if you like - also entering data into a form field and checking the validations.
-
-Quixote isn't quite that smart yet - but that's where I'd like to go. Right now is does basic assertions on logic with the typical "result.ShouldEqual(expectation)" as well as offering GET and POST functionality against your site - both with the option of sending in cookies.
-
-It's much easier than Watin, prettier than NUnit, and helps people who need to test their WebMatrix app (since there currently isn't a way to test a WebMatrix app). That's the focus - if you need deep testing on your super-swell architected Domain Model From Space - you should push that to a new project and use NUnit or XUnit. Quixote is a bit more high level.
-
-
-How To Install It?
-------------------
-Drop Quixote.cs into your App_Code directory (or a place where it can be compiled if you're not using WebMatrix). Then create a "tests" directory (which should be locked down!) and in there, add a page for your tests. Your test page is a simple Razor page - so call your first something like "happiness.cshtml".
-
-At the very top of this new Razor page, add a using statement:
-
- @using Quixote;
-
-Now you're ready to rock. Write a spec:
-
- @It.Should("rock me Amadeus")
-
-Now run the page. You're rockin!
-
-How Do You Use It?
-------------------
-You can use this for Acceptance Tests much easier than you can use something like Selenium or WatiN - and that's not a knock on those toolsets. They just take a bit to setup (Selenium requires Firefox and Java, WatiN relies on IE for everything). My goal with this is to make things drop-dead simple...
-
-The simplest thing to do is fire up a Console App in your project called "AcceptanceTests" and add the Quixote code file. You'll want access to the code ad you'll likely want to change a few things in there.
-
-Then, write some tests:
-
- class Program {
- static void Main(string[] args) {
- Runner.SiteRoot = "http://localhost:1701/";
- TheFollowing.Describes("Home Page");
-
- It.Should("Have 'VidPub' in the title", () => {
- return Runner.Get("/").Title.ShouldContain("VidPugb");
- });
-
- It.Should("Log me in with correct credentials", () => {
- var post = Runner.Post("/account/logon", new { login = "rob@tekpub.com", password = "password" });
- return post.Title.ShouldContain("Welcome");
- });
-
- Console.Read();
- }
- }
-
-POST and GET are available off of the Runner class and, pretty much do what you see here. You can also drop Quixote into your Web app (MVC or WebMatrix) and run your acceptance tests as a webpage.
-
-A typical flow would be to decide what it is you're doing before you do it. Crazy Talk. Let's say you're going to have a party:
-
- @TheFollowing.Describes("Drinks for the party")
- @They.Should("be bubbly")
- @They.Should("include some type of beer")
- @They.ShouldNot("be Belgian")
-
-When you run the page, the first line will output a nicely formatted title wrapped in an H2. Each spec under it will be marked as pending (and if you use the stylesheet inside of examples it will be yellow).
-
-We have some specs to work with - let's write our Party Code.
-(Presto Chango - DONE!)
-
-Now we can go back and write some assertions:
-
- @TheFollowing.Describes("Drinks for the party")
- @They.Should("be bubbly", () => {
- return Drinks.Find(1).Categories.ShouldContainString("Bubbly");
- })
-
- @They.Should("include some type of beer", () => {
- return Drinks.Find(1).Categories.First().ShouldEqual("Beer");
- })
-
- @They.ShouldNot("be Belgian", () => {
- return Drinks.ShouldNotContainItem(new Category("Belgian"));
- })
-
-Run the page again and your results will splash up on the screen. If you screw up and write bad code, the Exception will be wrapped up and tell you exactly where the problem is. If your test passes, it will be green, if it fails you will have a nice message telling you what the problem is.
-
-Note two things: the first is the use of "Func<Action,dynamic>" - this funky C# shorthand that says "I'm passing in some code that will return a dynamic result". Thus the "return" statement there - that MUST be there or the test won't compile. That returns a bit of love to the renderer so it knows what to do with your code.
-
-In the Examples directory are some examples of the Assertions - and you can just scroll down to the bottom of Quixote - they're all right there.
-
-We can also get freaky and run some functional tests against our site:
-
- @TheFollowing.Describes("the home page")
- @It.Should("say hello to me", () => {
- return Runner.Get("~/").Title.ShouldEqual("Hello Rob");
- })
- @It.Should("have 5 links", () => {
- return Runner.Get("~/").Links.ShouldHaveACountOf(5);
- })
- @It.Should("show me a funny thing if I post 42", () => {
- return Runner.Post("~/",new{EasterEgg = 42}).Body.ShouldContain("1337");
- })
-
-There's a bit more that's possible here - but I would probably start arm-waving. I'll build this out as time goes on (and as I use it) so I can extract usable things rather than what I think would be "neat".
-
+Quixote is a single-file, drop-in high-level test-runner for ASP.NET/WebMatrix using .NET 4.0
+=================================================================================================
+
+If you know Rails, you know RSpec and Cucumber. These high-level testing frameworks allow you to test how your app responds when people use your application (GET and POST requests, etc). These frameworks are pretty advanced - Cucumber allows you to simulate button and link clicks if you like - also entering data into a form field and checking the validations.
+
+Quixote isn't quite that smart yet - but that's where I'd like to go. Right now is does basic assertions on logic with the typical "result.ShouldEqual(expectation)" as well as offering GET and POST functionality against your site - both with the option of sending in cookies.
+
+It's much easier than Watin, prettier than NUnit, and helps people who need to test their WebMatrix app (since there currently isn't a way to test a WebMatrix app). That's the focus - if you need deep testing on your super-swell architected Domain Model From Space - you should push that to a new project and use NUnit or XUnit. Quixote is a bit more high level.
+
+
+How To Install It?
+------------------
+Drop Quixote.cs into your App_Code directory (or a place where it can be compiled if you're not using WebMatrix). Then create a "tests" directory (which should be locked down!) and in there, add a page for your tests. Your test page is a simple Razor page - so call your first something like "happiness.cshtml".
+
+At the very top of this new Razor page, add a using statement:
+
+ @using Quixote;
+
+Now you're ready to rock. Write a spec:
+
+ @It.Should("rock me Amadeus")
+
+Now run the page. You're rockin!
+
+How Do You Use It?
+------------------
+You can use this for Acceptance Tests much easier than you can use something like Selenium or WatiN - and that's not a knock on those toolsets. They just take a bit to setup (Selenium requires Firefox and Java, WatiN relies on IE for everything). My goal with this is to make things drop-dead simple...
+
+The simplest thing to do is fire up a Console App in your project called "AcceptanceTests" and add the Quixote code file. You'll want access to the code ad you'll likely want to change a few things in there.
+
+Then, write some tests:
+
+ class Program {
+ static void Main(string[] args) {
+ Runner.SiteRoot = "http://localhost:1701/";
+ TheFollowing.Describes("Home Page");
+
+ It.Should("Have 'VidPub' in the title", () => {
+ return Runner.Get("/").Title.ShouldContain("VidPugb");
+ });
+
+ It.Should("Log me in with correct credentials", () => {
+ var post = Runner.Post("/account/logon", new { login = "rob@tekpub.com", password = "password" });
+ return post.Title.ShouldContain("Welcome");
+ });
+
+ Console.Read();
+ }
+ }
+
+POST and GET are available off of the Runner class and, pretty much do what you see here. You can also drop Quixote into your Web app (MVC or WebMatrix) and run your acceptance tests as a webpage.
+
+A typical flow would be to decide what it is you're doing before you do it. Crazy Talk. Let's say you're going to have a party:
+
+ @TheFollowing.Describes("Drinks for the party")
+ @They.Should("be bubbly")
+ @They.Should("include some type of beer")
+ @They.ShouldNot("be Belgian")
+
+When you run the page, the first line will output a nicely formatted title wrapped in an H2. Each spec under it will be marked as pending (and if you use the stylesheet inside of examples it will be yellow).
+
+We have some specs to work with - let's write our Party Code.
+(Presto Chango - DONE!)
+
+Now we can go back and write some assertions:
+
+ @TheFollowing.Describes("Drinks for the party")
+ @They.Should("be bubbly", () => {
+ return Drinks.Find(1).Categories.ShouldContainString("Bubbly");
+ })
+
+ @They.Should("include some type of beer", () => {
+ return Drinks.Find(1).Categories.First().ShouldEqual("Beer");
+ })
+
+ @They.ShouldNot("be Belgian", () => {
+ return Drinks.ShouldNotContainItem(new Category("Belgian"));
+ })
+
+Run the page again and your results will splash up on the screen. If you screw up and write bad code, the Exception will be wrapped up and tell you exactly where the problem is. If your test passes, it will be green, if it fails you will have a nice message telling you what the problem is.
+
+Note two things: the first is the use of "Func<Action,dynamic>" - this funky C# shorthand that says "I'm passing in some code that will return a dynamic result". Thus the "return" statement there - that MUST be there or the test won't compile. That returns a bit of love to the renderer so it knows what to do with your code.
+
+In the Examples directory are some examples of the Assertions - and you can just scroll down to the bottom of Quixote - they're all right there.
+
+We can also get freaky and run some functional tests against our site:
+
+ @TheFollowing.Describes("the home page")
+ @It.Should("say hello to me", () => {
+ return Runner.Get("~/").Title.ShouldEqual("Hello Rob");
+ })
+ @It.Should("have 5 links", () => {
+ return Runner.Get("~/").Links.ShouldHaveACountOf(5);
+ })
+ @It.Should("show me a funny thing if I post 42", () => {
+ return Runner.Post("~/",new{EasterEgg = 42}).Body.ShouldContain("1337");
+ })
+
+There's a bit more that's possible here - but I would probably start arm-waving. I'll build this out as time goes on (and as I use it) so I can extract usable things rather than what I think would be "neat".
+
Please sign in to comment.
Something went wrong with that request. Please try again.