├── .gitignore ├── API ├── Console.cs ├── Context.cs ├── CookieJar.cs ├── Modules │ ├── ChildProcess.cs │ ├── FileSystem.cs │ ├── System.cs │ ├── WebPage.cs │ └── WebServer.cs ├── Phantom.cs ├── Trifle.cs └── Window.cs ├── Build ├── Binary │ ├── TrifleJS.Latest.zip │ ├── v0.1 │ │ └── TrifleJS.zip │ ├── v0.2 │ │ └── TrifleJS.zip │ ├── v0.3 │ │ └── TrifleJS.zip │ └── v0.4 │ │ └── TrifleJS.zip ├── Build.TrifleJS.bat └── Tools │ ├── 7-zip.chm │ ├── 7-zip.license.txt │ ├── 7za.exe │ ├── ILMerge License.rtf │ ├── ILMerge.doc │ ├── ILMerge.exe │ ├── makecert.exe │ └── makecert.exe.Licence.rtf ├── Callback.cs ├── Docs ├── IC16039.gif ├── Upgrade.to.VS2010 │ ├── Upgrade.To.VS2010.ERROR.png │ ├── Upgrade.To.VS2010.STEP1.png │ ├── Upgrade.To.VS2010.STEP2.png │ └── Upgrade.To.VS2010.STEP3.png ├── What is TrifleJS.doc ├── What.Is.Trifle.png ├── logo-150x150px.noletters.png ├── logo-200x200px.noletters.png ├── logo-260x260px.png ├── new.snk └── whatbrowser.org.IE8.png ├── Extensions.cs ├── LICENSE.MIT ├── Libs ├── Interop.IWshRuntimeLibrary.dll ├── Interop.SHDocVw.dll ├── Microsoft.VC90.CRT.manifest ├── Microsoft.mshtml.dll ├── Newtonsoft.Json.dll ├── Newtonsoft.Json.dll.LICENSE ├── Noesis.Javascript.dll ├── Noesis.Javascript.dll.LICENSE ├── msvcm90.dll ├── msvcp90.dll └── msvcr90.dll ├── Native ├── Browser.cs ├── COM.cs ├── CacheHelper.cs ├── Deprecated │ └── EnhancedBrowser.cs ├── IgnoreSSLErrorBrowser.cs ├── Methods.cs └── SkipDialogBrowser.cs ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs └── Resources.resx ├── Proxy.cs ├── README.md ├── TrifleJS.csproj ├── TrifleJS.sln ├── Utils.cs ├── examples ├── arguments.js ├── countdown.js ├── detectsniff.js ├── direction.js ├── fibo.js ├── follow.js ├── hello.js ├── injectme.js ├── loadspeed.js ├── module.js ├── phantomwebintro.js ├── postserver.js ├── printenv.js ├── printheaderfooter.js ├── printmargins.js ├── server.js ├── serverkeepalive.js ├── simpleserver.js ├── staticserver.js ├── test.js ├── universe.js ├── variable.js └── version.js ├── favicon.ico ├── includes ├── bootstrap.js └── trifle │ ├── Callback.js │ ├── ie │ ├── json2.js │ └── tools.js │ └── modules │ ├── ChildProcess.js │ ├── FileSystem.js │ ├── System.js │ ├── WebPage.js │ └── WebServer.js └── test ├── feature ├── console.log.js ├── fs.changeWorkingDirectory.js ├── opts.ignore-ssl.js ├── opts.proxy.js ├── page.clipRect.js ├── page.customHeaders.js ├── page.evaluate.js ├── page.evaluateJavaScript.js ├── page.eventLifecycle.js ├── page.includeJs.js ├── page.injectJs.js ├── page.js ├── page.onAlert.js ├── page.onCallback.js ├── page.onConfirm.js ├── page.open.js ├── page.open.x2.js ├── page.render.js ├── page.render.noscrollbar.js ├── page.viewportSize.js ├── page.zoomFactor.js ├── phantom.injectJs.js ├── phantom.libraryPath.js ├── system.js ├── webserver.listen.js ├── window.setInterval.js └── window.setTimeout.js ├── integration ├── casper │ └── TODO.txt └── phantom │ ├── LICENSE.BSD │ ├── README.md │ ├── examples │ ├── universe.js │ └── variable.js │ ├── feature │ ├── page.events.js │ └── page.rendering.js │ ├── phantomjs.exe │ ├── run.bat │ └── run.js ├── lib ├── MIT.LICENSE ├── chai.js ├── jasmine-console.js └── jasmine.js ├── ssl ├── CertEnroll.cs ├── CertInfo.cs ├── CertInfo.exe ├── HttpApi.cs └── prepare.ssl.bat.example └── unit ├── finish.js ├── phantom ├── run-jasmine.js ├── run-tests.js ├── scripts │ ├── frametest-harness.js │ └── frametest.js ├── spec │ ├── phantom.js │ ├── webpage.js │ └── webserver.js └── tools.js ├── ref ├── sample.html ├── sample_module.js └── script.js ├── spec ├── child_process.js ├── env.js ├── fs.js ├── phantom.js ├── require.js ├── ssl.js ├── system.js ├── webpage.js └── webserver.js └── tools.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.meta 24 | *.obj 25 | *.pch 26 | *.pdb 27 | *.pgc 28 | *.pgd 29 | *.rsp 30 | *.sbr 31 | *.tli 32 | *.tlh 33 | *.tmp 34 | *.log 35 | *.vspscc 36 | *.vssscc 37 | .builds 38 | 39 | # Visual C++ cache files 40 | ipch/ 41 | *.aps 42 | *.ncb 43 | *.opensdf 44 | *.sdf 45 | 46 | # Visual Studio profiler 47 | *.psess 48 | *.vsp 49 | *.vspx 50 | 51 | # Guidance Automation Toolkit 52 | *.gpState 53 | 54 | # ReSharper is a .NET coding add-in 55 | _ReSharper* 56 | 57 | # NCrunch 58 | *.ncrunch* 59 | .*crunch*.local.xml 60 | 61 | # Installshield output folder 62 | [Ee]xpress 63 | 64 | # DocProject is a documentation generator add-in 65 | DocProject/buildhelp/ 66 | DocProject/Help/*.HxT 67 | DocProject/Help/*.HxC 68 | DocProject/Help/*.hhc 69 | DocProject/Help/*.hhk 70 | DocProject/Help/*.hhp 71 | DocProject/Help/Html2 72 | DocProject/Help/html 73 | 74 | # Click-Once directory 75 | publish 76 | 77 | # Publish Web Output 78 | *.Publish.xml 79 | 80 | # NuGet Packages Directory 81 | packages 82 | 83 | # Windows Azure Build Output 84 | csx 85 | *.build.csdef 86 | 87 | # Windows Store app package directory 88 | AppPackages/ 89 | 90 | # Others 91 | [Bb]in 92 | [Oo]bj 93 | sql 94 | TestResults 95 | [Tt]est[Rr]esult* 96 | *.Cache 97 | ClientBin 98 | [Ss]tyle[Cc]op.* 99 | ~$* 100 | *.dbmdl 101 | Generated_Code #added for RIA/Silverlight projects 102 | 103 | # Backup & report files from converting an old project file to a newer 104 | # Visual Studio version. Backup files are not needed, because we have git ;-) 105 | _UpgradeReport_Files/ 106 | Backup*/ 107 | UpgradeLog*.XML 108 | 109 | /.idea -------------------------------------------------------------------------------- /API/Console.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TrifleJS.API 6 | { 7 | /// 8 | /// Defines functionality for the javascript 'console' object. 9 | /// 10 | public class Console 11 | { 12 | public static void debug(object value) 13 | { 14 | StdOut(value, ConsoleColor.DarkCyan); 15 | } 16 | 17 | public static void xdebug(object value) 18 | { 19 | Utils.Debug(String.Format("{0}", Parse(value))); 20 | } 21 | 22 | public static void log(object value) 23 | { 24 | StdOut(value, null); 25 | } 26 | 27 | public static void error(object value) 28 | { 29 | StdErr(value, ConsoleColor.Red); 30 | } 31 | 32 | public static void warn(object value) 33 | { 34 | StdOut(value, ConsoleColor.Yellow); 35 | } 36 | 37 | public static void color(string color, object value) 38 | { 39 | ConsoleColor consoleColor = ConsoleColor.Gray; 40 | try 41 | { 42 | consoleColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), color, true); 43 | } 44 | catch { } 45 | finally { 46 | StdOut(value, consoleColor); 47 | } 48 | } 49 | 50 | public static void clear() 51 | { 52 | System.Console.Clear(); 53 | } 54 | 55 | private static void StdOut(object value, ConsoleColor? color) 56 | { 57 | Std(value, color, false); 58 | } 59 | 60 | private static void StdErr(object value, ConsoleColor? color) 61 | { 62 | Std(value, color, true); 63 | } 64 | 65 | private static void Std(object value, ConsoleColor? color, bool err) 66 | { 67 | ConsoleColor normalColor = System.Console.ForegroundColor; 68 | if (color != null) { System.Console.ForegroundColor = (ConsoleColor)color; } 69 | if (err) { System.Console.Error.WriteLine("{0}", Parse(value)); } 70 | else { System.Console.WriteLine("{0}", Parse(value)); } 71 | System.Console.ForegroundColor = normalColor; 72 | } 73 | 74 | /// 75 | /// Parses an object for output to the system console 76 | /// 77 | /// 78 | /// 79 | public static object Parse(object value) 80 | { 81 | if (value == null) 82 | { 83 | return "null"; 84 | } 85 | string type = value.GetType().ToString(); 86 | switch (type) 87 | { 88 | case "System.String": 89 | // Fix path separator 90 | return ((String)value).Replace("\\", "\\\\"); 91 | case "System.Boolean": 92 | case "System.Int32": 93 | case "System.Int64": 94 | case "System.Double": 95 | return value; 96 | default: 97 | return Utils.Serialize(value); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /API/Context.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.IO; 6 | using Noesis.Javascript; 7 | 8 | namespace TrifleJS.API 9 | { 10 | /// 11 | /// Core JavaScript engine context 12 | /// 13 | public class Context : JavascriptContext 14 | { 15 | /// 16 | /// Executes a javascript file 17 | /// 18 | /// Path to file, this can either be relative to triflejs.exe or current script being executed. 19 | /// 20 | public object RunFile(string filepath) 21 | { 22 | FileInfo file = Find(filepath); 23 | if (file == null || !file.Exists) 24 | { 25 | throw new Exception(String.Format("File does not exist {0}.", filepath)); 26 | } 27 | // Read file 28 | string script = File.ReadAllText(file.FullName, Context.Encoding ?? Encoding.UTF8); 29 | // Tag which file we are executing 30 | Phantom.scriptName = file.Name; 31 | // Execute file 32 | return RunScript(script, file.Name); 33 | } 34 | 35 | /// 36 | /// Executes a script 37 | /// 38 | /// String containing javascript to execute 39 | /// Name of the script 40 | public object RunScript(string script, string scriptName) 41 | { 42 | // Read file and add a blank function at the end, 43 | // this is to fix a stackoverflow bug 44 | // in Javascript.NET where it tries 45 | // to return an object with circular reference 46 | script += ";(function() {})();"; 47 | // Execute 48 | return base.Run(script, scriptName); 49 | } 50 | 51 | /// 52 | /// Finds a file either in the current working directory or in the LibraryPath. 53 | /// Returns null if not found. 54 | /// 55 | /// path to find 56 | /// 57 | public FileInfo Find(string path) 58 | { 59 | FileInfo file = new FileInfo(path); 60 | if (!file.Exists) 61 | { 62 | string currentDir = Environment.CurrentDirectory; 63 | Environment.CurrentDirectory = Phantom.libraryPath; 64 | file = new FileInfo(path); 65 | Environment.CurrentDirectory = currentDir; 66 | } 67 | return file; 68 | } 69 | 70 | /// 71 | /// Gets the current context 72 | /// 73 | public static Context Current 74 | { 75 | get { return Program.Context; } 76 | } 77 | 78 | /// 79 | /// Encoding for reading script files 80 | /// 81 | /// 82 | public static Encoding Encoding { get; set; } 83 | 84 | /// 85 | /// Handles an exception 86 | /// 87 | /// 88 | public static void Handle(Exception ex) 89 | { 90 | // Default error handler simply logs error to console. 91 | // This can be replaced using phantom.onError = function(msg, trace) {} 92 | ContextError err = new ContextError(ex); 93 | Phantom._fireEvent("error", err.message, err.trace); 94 | } 95 | 96 | /// 97 | /// Parses input/output to make it JavaScript friendly 98 | /// 99 | /// an array of argument objects (of any type) 100 | /// list of parsed arguments 101 | public static string[] Parse(params object[] arguments) 102 | { 103 | List input = new List(); 104 | foreach (object argument in arguments) 105 | { 106 | input.Add(ParseOne(argument)); 107 | } 108 | return input.ToArray(); 109 | } 110 | 111 | /// 112 | /// Parses input/output to make it JavaScript friendly 113 | /// 114 | /// an argument object (of any type) 115 | /// the parsed argument 116 | public static string ParseOne(object argument) 117 | { 118 | if (argument == null) 119 | { 120 | return "null"; 121 | } 122 | else 123 | { 124 | switch (argument.GetType().Name) 125 | { 126 | case "Int32": 127 | case "Double": 128 | return argument.ToString(); 129 | case "DateTime": 130 | return String.Format("new Date({0})", Utils.Serialize(argument)); 131 | case "Boolean": 132 | return argument.ToString().ToLowerInvariant(); 133 | case "String": 134 | // Fix for undefined (coming up as null) 135 | if ("{{undefined}}".Equals(argument)) 136 | { 137 | return "undefined"; 138 | } 139 | else 140 | { 141 | return String.Format("\"{0}\"", argument.ToString().Replace("\"", "\\\"")); 142 | } 143 | default: 144 | return Utils.Serialize(argument); 145 | } 146 | } 147 | } 148 | 149 | } 150 | 151 | /// 152 | /// Error info as used in page.onError and phantom.onError handlers 153 | /// 154 | public class ContextError 155 | { 156 | public ContextError(Exception ex) 157 | { 158 | message = ex.Message; 159 | trace = new List(); 160 | JavascriptException jsEx = ex as JavascriptException; 161 | if (jsEx != null) 162 | { 163 | // Remove refs to Host environment & output javascript error 164 | message = jsEx.Message.Replace("TrifleJS.Host+", ""); 165 | trace.Add(new TraceData 166 | { 167 | file = jsEx.Source, 168 | line = jsEx.Line, 169 | col = jsEx.StartColumn, 170 | function = jsEx.TargetSite.Name 171 | }); 172 | } 173 | else 174 | { 175 | foreach (var frame in new StackTrace(ex).GetFrames()) 176 | { 177 | trace.Add(new TraceData 178 | { 179 | file = frame.GetFileName(), 180 | line = frame.GetFileLineNumber(), 181 | col = frame.GetFileColumnNumber(), 182 | function = frame.GetMethod().Name 183 | }); 184 | } 185 | } 186 | } 187 | public string message; 188 | public List trace; 189 | public class TraceData 190 | { 191 | public string file; 192 | public int line; 193 | public int col; 194 | public string function; 195 | } 196 | } 197 | 198 | 199 | 200 | } 201 | -------------------------------------------------------------------------------- /API/CookieJar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.IO; 6 | using TrifleJS.Native; 7 | 8 | namespace TrifleJS.API 9 | { 10 | public class CookieJar 11 | { 12 | public static CookieJar Current = new CookieJar(); 13 | 14 | public bool Enabled { get; set; } 15 | public Dictionary> content = new Dictionary>(); 16 | 17 | public CookieJar() { } 18 | 19 | public CookieJar(string file) { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | public bool Add(Dictionary data) 24 | { 25 | return Add(data, null); 26 | } 27 | 28 | public bool Add(Cookie cookie) { 29 | return Add(cookie, null); 30 | } 31 | 32 | public bool Add(Dictionary data, string url) 33 | { 34 | if (data != null) 35 | { 36 | Cookie cookie = new Cookie(); 37 | cookie.Load(data); 38 | return Add(cookie, url); 39 | } 40 | return false; 41 | } 42 | 43 | public bool Add(Cookie cookie, string url) { 44 | if (Enabled && cookie != null) 45 | { 46 | if (String.IsNullOrEmpty(url)) 47 | { 48 | // Empty URL? Look for it in domain 49 | url = cookie.GetUrl(); 50 | } 51 | if (Browser.TryParse(url ?? "") != null) 52 | { 53 | if (cookie.Save()) 54 | { 55 | if (content.ContainsKey(url)) 56 | { 57 | content[url].Add(cookie); 58 | } 59 | else 60 | { 61 | content.Add(url, new List { cookie }); 62 | } 63 | } 64 | return true; 65 | } 66 | } 67 | return false; 68 | } 69 | 70 | /// 71 | /// Clears cookies from all browser sessions 72 | /// 73 | public void ClearAll() { 74 | content = new Dictionary>(); 75 | Native.Methods.ResetBrowserSession(IntPtr.Zero); 76 | } 77 | 78 | /// 79 | /// Clears cookies for a specific URI 80 | /// 81 | /// 82 | public void Clear(Uri targetUri) { 83 | if (targetUri != null) 84 | { 85 | foreach (string url in content.Keys) { 86 | Uri uri = Browser.TryParse(url); 87 | if (uri.Host == targetUri.Host) { 88 | content.Remove(url); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | /// 96 | /// Extensions to .NET Cookie class for Cookie Jar functionality 97 | /// 98 | public static class CookieExtensions 99 | { 100 | /// 101 | /// Loads the contents of a cookie dictionary into a system cookie 102 | /// 103 | /// 104 | /// 105 | public static void Load(this Cookie cookie, Dictionary data) 106 | { 107 | if (data != null) 108 | { 109 | cookie.Name = data.Get("name"); 110 | cookie.Value = data.Get("value"); 111 | cookie.Secure = data.Get("secure"); 112 | cookie.Domain = data.Get("domain"); 113 | cookie.Path = String.IsNullOrEmpty(data.Get("path")) ? "/" : data.Get("path"); 114 | cookie.Expires = data.Get("expires"); 115 | cookie.HttpOnly = data.Get("httpOnly"); 116 | } 117 | } 118 | 119 | /// 120 | /// Converts a cookie into a dictionary for display 121 | /// 122 | /// 123 | /// 124 | public static Dictionary ToDictionary(this Cookie cookie) 125 | { 126 | Dictionary data = new Dictionary(); 127 | data.Add("name", cookie.Name); 128 | data.Add("value", cookie.Value); 129 | data.Add("secure", cookie.Secure); 130 | data.Add("domain", cookie.Domain); 131 | data.Add("path", cookie.Path); 132 | if (cookie.Expires > DateTime.Now) { data.Add("expires", cookie.Expires); } 133 | data.Add("httpOnly", cookie.HttpOnly); 134 | return data; 135 | } 136 | 137 | /// 138 | /// Returns url for the cookie (using cookie domain and path) 139 | /// 140 | /// 141 | /// 142 | public static string GetUrl(this Cookie cookie) 143 | { 144 | if (!String.IsNullOrEmpty(cookie.Domain)) 145 | { 146 | return String.Format("http{0}://{1}{2}{3}", 147 | cookie.Secure ? "s" : "", 148 | cookie.Domain.StartsWith(".") ? "www" : "", 149 | cookie.Domain, 150 | String.IsNullOrEmpty(cookie.Path) ? "/" : cookie.Path); 151 | } 152 | return null; 153 | } 154 | 155 | /// 156 | /// Saves a cookie for use in .NET WebBrowser (via Windows API) 157 | /// 158 | /// 159 | /// 160 | public static bool Save(this Cookie cookie) 161 | { 162 | // Make sure we only set cookie for duration of the session 163 | // @see http://stackoverflow.com/questions/1780469/how-do-i-set-cookie-expiration-to-session-in-c 164 | DateTime expires = cookie.Expires; 165 | cookie.Expires = DateTime.MinValue; 166 | bool success = false; 167 | try 168 | { 169 | string url = cookie.GetUrl(); 170 | string cookieString = cookie.ToString(); 171 | Native.Methods.InternetSetCookie(cookie.GetUrl(), null, cookie.ToString()); 172 | success = true; 173 | } 174 | catch { } 175 | finally 176 | { 177 | cookie.Expires = expires; 178 | } 179 | return success; 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /API/Modules/System.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Runtime.InteropServices; 7 | 8 | namespace TrifleJS.API.Modules 9 | { 10 | /// 11 | /// Defines a set of system tools 12 | /// @see https://github.com/ariya/phantomjs/wiki/API-Reference-System 13 | /// 14 | public class System 15 | { 16 | /// 17 | /// Constructor 18 | /// 19 | public System() { 20 | this.os = new Dictionary(); 21 | this.os.Add("architecture", GetArchitecture()); 22 | this.os.Add("name", "windows"); 23 | this.os.Add("version", GetVersion()); 24 | } 25 | 26 | /// 27 | /// Returns the arguments passed when executing triflejs.exe in the console 28 | /// 29 | public string[] args 30 | { 31 | get { return Program.Args.ToArray(); } 32 | } 33 | 34 | /// 35 | /// Returns the Thread ID for the currently executing OS process. 36 | /// 37 | public int pid { 38 | get { return AppDomain.GetCurrentThreadId(); } 39 | } 40 | 41 | /// 42 | /// Returns a list of environmental variables 43 | /// 44 | public IDictionary env { 45 | get { 46 | Dictionary envVars = new Dictionary(); 47 | foreach(DictionaryEntry entry in Environment.GetEnvironmentVariables()) { 48 | envVars[(string)entry.Key] = entry.Value as string; 49 | } 50 | return envVars; 51 | } 52 | } 53 | 54 | /// 55 | /// Internal platform, always "phantomjs" 56 | /// 57 | public string platform { 58 | get { 59 | return "phantomjs"; 60 | } 61 | } 62 | 63 | private string _blah; 64 | public string blah(string text) { 65 | this._blah = text; 66 | return text; 67 | } 68 | 69 | /// 70 | /// Operating system information 71 | /// 72 | public Dictionary os { 73 | get; set; 74 | } 75 | 76 | private string GetArchitecture() { 77 | // Clearly if this is a 64-bit process we must be on a 64-bit OS. 78 | if (IntPtr.Size == 8) return "64bit"; 79 | // Ok, so we are a 32-bit process, but is the OS 64-bit? 80 | // If we are running under Wow64 than the OS is 64-bit. 81 | bool isWow64; 82 | if (ModuleContainsFunction("kernel32.dll", "IsWow64Process") && IsWow64Process(GetCurrentProcess(), out isWow64) && isWow64) 83 | { 84 | return "64bit"; 85 | } 86 | else { 87 | return "32bit"; 88 | } 89 | } 90 | private bool ModuleContainsFunction(string moduleName, string methodName) 91 | { 92 | IntPtr hModule = GetModuleHandle(moduleName); 93 | if (hModule != IntPtr.Zero) 94 | return GetProcAddress(hModule, methodName) != IntPtr.Zero; 95 | return false; 96 | } 97 | [DllImport("kernel32.dll", SetLastError = true)] 98 | [return: MarshalAs(UnmanagedType.Bool)] 99 | extern static bool IsWow64Process(IntPtr hProcess, [MarshalAs(UnmanagedType.Bool)] out bool isWow64); 100 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 101 | extern static IntPtr GetCurrentProcess(); 102 | [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 103 | extern static IntPtr GetModuleHandle(string moduleName); 104 | [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] 105 | extern static IntPtr GetProcAddress(IntPtr hModule, string methodName); 106 | 107 | /// 108 | /// Gets the system version (XP, Vista, 7 etc) 109 | /// 110 | /// 111 | private string GetVersion() { 112 | 113 | //Get Operating system information. 114 | OperatingSystem os = Environment.OSVersion; 115 | //Get version information about the os. 116 | Version vs = os.Version; 117 | 118 | if (os.Platform == PlatformID.Win32Windows) 119 | { 120 | //This is a pre-NT version of Windows 121 | switch (vs.Minor) 122 | { 123 | case 0: 124 | return "95"; 125 | case 10: 126 | return "98"; 127 | case 90: 128 | return "me"; 129 | default: 130 | break; 131 | } 132 | } 133 | else if (os.Platform == PlatformID.Win32NT) 134 | { 135 | switch (vs.Major) 136 | { 137 | case 3: 138 | case 4: 139 | return "nt"; 140 | case 5: 141 | if (vs.Minor == 0) 142 | return "2000"; 143 | else 144 | return "xp"; 145 | case 6: 146 | switch (vs.Minor) 147 | { 148 | case 0: 149 | return "vista"; 150 | case 1: 151 | return "7"; 152 | case 2: 153 | return "8"; 154 | case 3: 155 | return "8.1"; 156 | default: 157 | break; 158 | } 159 | break; 160 | default: 161 | break; 162 | } 163 | } 164 | return "unknown"; 165 | } 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /API/Trifle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Forms; 4 | using System.Threading; 5 | 6 | namespace TrifleJS.API 7 | { 8 | /// 9 | /// Trifle Class 10 | /// 11 | public class Trifle 12 | { 13 | /// 14 | /// Returns the current versions 15 | /// 16 | public static Dictionary Version 17 | { 18 | get 19 | { 20 | return new Dictionary { 21 | {"major", 0}, 22 | {"minor", 5}, 23 | {"patch", 0} 24 | }; 25 | } 26 | } 27 | 28 | /// 29 | /// Version of Internet Explorer currently being emulated. 30 | /// 31 | public static string Emulation { get; set; } 32 | 33 | /// 34 | /// Suspends V8 execution and runs program event loop 35 | /// 36 | /// Milliseconds to wait for 37 | public static void Wait(int milliseconds) 38 | { 39 | int now = Environment.TickCount; 40 | while (Environment.TickCount < now + milliseconds) { 41 | Program.DoEvents(); 42 | } 43 | } 44 | 45 | /// 46 | /// Used to ignore SSL errors 47 | /// 48 | public static bool IgnoreSSLErrors 49 | { 50 | set 51 | { 52 | Native.Browser.IgnoreSSLErrors = value; 53 | } 54 | } 55 | 56 | // These are a set of C# mid-tier classes that can be instantiated 57 | // inside the javascript engine as CommonJS Modules. 58 | 59 | public Modules.WebPage WebPage() 60 | { 61 | return new Modules.WebPage(); 62 | } 63 | 64 | public Modules.FileSystem FileSystem() 65 | { 66 | if (!Program.SecureMode) 67 | { 68 | return new Modules.FileSystem(); 69 | } 70 | throw new Exception("--secure-mode: FileSystem module has been disabled."); 71 | } 72 | 73 | public Modules.System System() 74 | { 75 | return new Modules.System(); 76 | } 77 | 78 | public Modules.WebServer WebServer() 79 | { 80 | return new Modules.WebServer(); 81 | } 82 | 83 | public Modules.ChildProcess ChildProcess() 84 | { 85 | if (!Program.SecureMode) 86 | { 87 | return new Modules.ChildProcess(); 88 | } 89 | throw new Exception("--secure-mode: ChildProcess module has been disabled."); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /API/Window.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TrifleJS.API 6 | { 7 | public class Window 8 | { 9 | private Modules.WebPage page; 10 | 11 | public Window() { 12 | page = new Modules.WebPage(); 13 | } 14 | 15 | /// 16 | /// List of currently executing timers for window.setTimeout() and window.setInterval() 17 | /// 18 | public static Dictionary timers = new Dictionary(); 19 | 20 | /// 21 | /// Internal Id used for window.clearTimeout() and window.clearInterval() 22 | /// 23 | private static int timerId = 0; 24 | 25 | /// 26 | /// Defers execution of a callback by number of milliseconds, same as window.setTimeout() 27 | /// 28 | /// ID of the callback in JavaScript context 29 | /// Millseconds to wait for 30 | /// 31 | public static int SetTimeout(string callbackId, int ms) { 32 | return SetTimer(callbackId, ms, true); 33 | } 34 | 35 | /// 36 | /// Executes a callback at repeated intervals, same as window.setInterval() 37 | /// 38 | /// ID of the callback in JavaScript context 39 | /// >Millseconds between intervals 40 | /// 41 | public static int SetInterval(string callbackId, int ms) { 42 | return SetTimer(callbackId, ms, false); 43 | } 44 | 45 | /// 46 | /// Clears a timeout specified with SetTimeout 47 | /// 48 | /// ID of the timeout call 49 | public static void ClearTimeout(int timeoutId) { 50 | ClearTimer(timeoutId); 51 | } 52 | 53 | /// 54 | /// Clears execution specified with SetInterval 55 | /// 56 | /// 57 | public static void ClearInterval(int intervalId) { 58 | ClearTimer(intervalId); 59 | } 60 | 61 | /// 62 | /// Returns the navigator object 63 | /// 64 | public object navigator { 65 | get { 66 | string info = page._evaluate(@" 67 | function() { 68 | var val, output = {}; 69 | for (var prop in window.navigator) { 70 | val = window.navigator[prop]; 71 | if (typeof val !== 'object') { 72 | output[prop] = val; 73 | } 74 | } 75 | return output; 76 | }", new object[] { }) as string; 77 | return Utils.Deserialize(info); 78 | } 79 | } 80 | 81 | /// 82 | /// Returns the location object 83 | /// 84 | public object location 85 | { 86 | get 87 | { 88 | return new Dictionary { 89 | {"hash", ""}, 90 | {"host", ""}, 91 | {"hostname", ""}, 92 | {"href", "file://" + Phantom.scriptName ?? "/"}, 93 | {"origin", "null"}, 94 | {"pathname", Phantom.scriptName ?? "/"}, 95 | {"port", ""}, 96 | {"protocol", "file://"}, 97 | {"search", ""} 98 | }; 99 | } 100 | } 101 | 102 | private static int SetTimer(string callbackId, int ms, bool once) { 103 | Timer timer = new Timer(); 104 | timer.Callback += delegate 105 | { 106 | if (once) { timer.Enabled = false; } 107 | Callback.Execute(callbackId, once, null); 108 | }; 109 | timer.Enabled = true; 110 | timer.Interval = ms; 111 | timer.Start(); 112 | Window.timers.Add(++timerId, timer); 113 | return timerId; 114 | } 115 | 116 | private static void ClearTimer(int timerId) { 117 | Timer timer = Window.timers[timerId]; 118 | if (timer != null) { 119 | timer.Dispose(); 120 | Window.timers.Remove(timerId); 121 | } 122 | } 123 | 124 | /// 125 | /// Internal timer for window.setTimeout() and window.setInterval(). 126 | /// This is to ensure that async calls always run on the same thread. 127 | /// 128 | public class Timer : IDisposable 129 | { 130 | 131 | public void Tick() 132 | { 133 | if (Enabled && Environment.TickCount >= nextTick) 134 | { 135 | Callback.Invoke(this, null); 136 | nextTick = Environment.TickCount + Interval; 137 | } 138 | } 139 | 140 | private int nextTick = 0; 141 | 142 | public void Start() 143 | { 144 | this.Enabled = true; 145 | Interval = interval; 146 | } 147 | 148 | public void Stop() 149 | { 150 | this.Enabled = false; 151 | } 152 | 153 | public event EventHandler Callback; 154 | 155 | public bool Enabled = false; 156 | 157 | private int interval = 1000; 158 | 159 | public int Interval 160 | { 161 | get { return interval; } 162 | set { interval = value; nextTick = Environment.TickCount + interval; } 163 | } 164 | 165 | public void Dispose() 166 | { 167 | this.Callback = null; 168 | this.Stop(); 169 | } 170 | 171 | } 172 | 173 | /// 174 | /// Checks through all available timers 175 | /// to see if any of them should be executed. 176 | /// 177 | public static void CheckTimers() 178 | { 179 | foreach (Timer timer in new List(timers.Values)) 180 | { 181 | timer.Tick(); 182 | } 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /Build/Binary/TrifleJS.Latest.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Binary/TrifleJS.Latest.zip -------------------------------------------------------------------------------- /Build/Binary/v0.1/TrifleJS.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Binary/v0.1/TrifleJS.zip -------------------------------------------------------------------------------- /Build/Binary/v0.2/TrifleJS.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Binary/v0.2/TrifleJS.zip -------------------------------------------------------------------------------- /Build/Binary/v0.3/TrifleJS.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Binary/v0.3/TrifleJS.zip -------------------------------------------------------------------------------- /Build/Binary/v0.4/TrifleJS.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Binary/v0.4/TrifleJS.zip -------------------------------------------------------------------------------- /Build/Build.TrifleJS.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | :: Define variables 4 | 5 | SET CompilePath=..\bin\Release 6 | SET MergePath=.\Merge 7 | SET BinaryPath=.\Binary 8 | SET ILMerge=Tools\ILMerge.exe 9 | SET Zip=Tools\7za.exe 10 | SET MsBuild40=C:\Windows\Microsoft.NET\Framework\v4.0.30319\MsBuild.exe 11 | SET WinSDK70A=C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\. 12 | SET WinSDK71=C:\Program Files\Microsoft SDKs\Windows\v7.1\bin\. 13 | 14 | :: Check Windows SDK 15 | 16 | IF EXIST "%WinSDK70A%" ( 17 | SET "WinSDK=%WinSDK70A%" 18 | ) ELSE IF EXIST "%WinSDK71%" ( 19 | SET "WinSDK=%WinSDK71%" 20 | ) ELSE ( 21 | ECHO Please install Windows SDK 2010. 22 | ECHO http://www.microsoft.com/en-us/download/details.aspx?id=8279 23 | EXIT /b 1 24 | ) 25 | 26 | ECHO Using Windows SDK at %WinSDK% 27 | 28 | :: Check for MsBuild 29 | 30 | IF EXIST "%MsBuild40%" ( 31 | SET "MsBuild=%MsBuild40%" 32 | ) ELSE ( 33 | ECHO Cannot Find MsBuild! 34 | EXIT /b 1 35 | ) 36 | 37 | ECHO Using MsBuild at %MsBuild% 38 | 39 | :: Clean up last release 40 | 41 | RMDIR /S /Q %CompilePath% 42 | RMDIR /S /Q %MergePath% 43 | DEL %BinaryPath%\*.* /Q /F 44 | MKDIR %MergePath% 45 | MKDIR %BinaryPath% 46 | 47 | :: Run MSBuild 48 | 49 | %MsBuild% ..\TrifleJS.csproj /t:Rebuild /p:Configuration=Release 50 | 51 | IF EXIST "%CompilePath%\TrifleJS.exe" ( 52 | ECHO Compilation Ok! 53 | ) ELSE ( 54 | ECHO Compilation Fail. 55 | EXIT /b 56 | ) 57 | 58 | :: Prepare & Zip Release 59 | XCOPY %CompilePath%\*.* %MergePath% /Y /E 60 | DEL %MergePath%\TrifleJS.exe /Q 61 | DEL %MergePath%\Interop.IWshRuntimeLibrary.dll /Q 62 | DEL %MergePath%\Interop.CERTENROLLLib.dll /Q 63 | DEL %MergePath%\Interop.SHDocVw.dll /Q 64 | DEL %MergePath%\Newtonsoft.Json.dll /Q 65 | 66 | ECHO 67 | ECHO Merging and zipping to \Build\Binary directory. 68 | ECHO NOTE: This will take about 2 minutes.. 69 | 70 | %ILMerge% /out:%MergePath%\TrifleJS.exe %CompilePath%\TrifleJS.exe %CompilePath%\Interop.IWshRuntimeLibrary.dll %CompilePath%\Interop.CERTENROLLLib.dll %CompilePath%\Interop.SHDocVw.dll %CompilePath%\Newtonsoft.Json.dll 71 | DEL %MergePath%\TrifleJS.pdb 72 | CD %MergePath% 73 | ..\%Zip% a ..\Binary\TrifleJS.Latest.zip -r *.* 74 | CD .. 75 | RMDIR /S /Q %MergePath% 76 | 77 | ECHO SUCCESS!! 78 | 79 | -------------------------------------------------------------------------------- /Build/Tools/7-zip.chm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Tools/7-zip.chm -------------------------------------------------------------------------------- /Build/Tools/7-zip.license.txt: -------------------------------------------------------------------------------- 1 | 7-Zip Command line version 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | License for use and distribution 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | 7-Zip Copyright (C) 1999-2010 Igor Pavlov. 7 | 8 | 7za.exe is distributed under the GNU LGPL license 9 | 10 | Notes: 11 | You can use 7-Zip on any computer, including a computer in a commercial 12 | organization. You don't need to register or pay for 7-Zip. 13 | 14 | 15 | GNU LGPL information 16 | -------------------- 17 | 18 | This library is free software; you can redistribute it and/or 19 | modify it under the terms of the GNU Lesser General Public 20 | License as published by the Free Software Foundation; either 21 | version 2.1 of the License, or (at your option) any later version. 22 | 23 | This library is distributed in the hope that it will be useful, 24 | but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 26 | Lesser General Public License for more details. 27 | 28 | You can receive a copy of the GNU Lesser General Public License from 29 | http://www.gnu.org/ 30 | -------------------------------------------------------------------------------- /Build/Tools/7za.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Tools/7za.exe -------------------------------------------------------------------------------- /Build/Tools/ILMerge.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Tools/ILMerge.doc -------------------------------------------------------------------------------- /Build/Tools/ILMerge.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Tools/ILMerge.exe -------------------------------------------------------------------------------- /Build/Tools/makecert.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Tools/makecert.exe -------------------------------------------------------------------------------- /Build/Tools/makecert.exe.Licence.rtf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Build/Tools/makecert.exe.Licence.rtf -------------------------------------------------------------------------------- /Callback.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Security.Permissions; 5 | using System.Runtime.InteropServices; 6 | using System.Web.Script.Serialization; 7 | using Noesis.Javascript; 8 | using TrifleJS.API; 9 | 10 | namespace TrifleJS 11 | { 12 | public class Callback 13 | { 14 | /// 15 | /// A queue of pending callbacks that should be executed in 16 | /// the V8 environment. This is used for event handling on 17 | /// modules that use a separate thread (ie. ChildProcess), 18 | /// as making the callback inside those threads does not 19 | /// expose our current V8 environment. 20 | /// 21 | public static Queue queue = new Queue(); 22 | 23 | /// 24 | /// Adds items to the callback queue 25 | /// 26 | /// 27 | /// 28 | public static void Queue(string id, bool once, params object[] arguments) 29 | { 30 | queue.Enqueue(new Callback { Id = id, Once = once, Arguments = arguments } ); 31 | } 32 | 33 | /// 34 | /// Processes the callback queue 35 | /// (used in the event loop) 36 | /// 37 | public static void ProcessQueue() 38 | { 39 | while (queue.Count > 0) 40 | { 41 | Callback callback = queue.Dequeue(); 42 | Callback.Execute(callback.Id, callback.Once, callback.Arguments); 43 | } 44 | } 45 | 46 | /// 47 | /// Used for queueing callbacks 48 | /// 49 | internal string Id {get; set;} 50 | internal bool Once { get; set; } 51 | internal object[] Arguments { get; set; } 52 | 53 | /// 54 | /// Allows callback from C# middleware to the V8 JavaScript Runtime. 55 | /// Deletes the original callback function. 56 | /// 57 | /// Callback id 58 | /// any arguments to pass to the callback 59 | /// 60 | public static bool ExecuteOnce(string id, params object[] arguments) 61 | { 62 | return Execute(id, true, arguments); 63 | } 64 | 65 | /// 66 | /// Allows callback from C# middleware to the V8 JavaScript Runtime. 67 | /// Keeps the original callback function to allow multiple execution. 68 | /// 69 | /// Callback id 70 | /// any arguments to pass to the callback 71 | /// 72 | public static bool Execute(string id, params object[] arguments) 73 | { 74 | return Execute(id, false, arguments); 75 | } 76 | 77 | /// 78 | /// Executes a callback 79 | /// 80 | public static bool Execute(string id, bool once, params object[] arguments) 81 | { 82 | try 83 | { 84 | if (arguments == null) { arguments = new object[0]; } 85 | String cmd = String.Format(@"trifle.Callback.{0}('{1}', [{2}]);", 86 | once ? "executeOnce" : "execute", 87 | id, 88 | String.Join(",", Context.Parse(arguments)) 89 | ); 90 | Program.Context.Run(cmd, "Callback#" + id); 91 | } 92 | catch (Exception ex) { 93 | API.Context.Handle(ex); 94 | return false; 95 | } 96 | return true; 97 | } 98 | 99 | 100 | /// 101 | /// Allows callbacks from IE to C# and the V8 Javascript Runtime, 102 | /// PLEASE NOTE: Debugging inside this class is likely to 103 | /// make IE loose focus, causing unexpected behaviour. 104 | /// 105 | [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] 106 | [ComVisible(true)] 107 | public class External 108 | { 109 | /// 110 | /// Initialises the ObjectForScripting class 111 | /// 112 | /// A page where scripts are being executed 113 | public External(API.Modules.WebPage page) { 114 | this.page = page; 115 | } 116 | public API.Modules.WebPage page; 117 | /// 118 | /// Outputs a debug message 119 | /// 120 | /// 121 | public void xdebug(string message) { 122 | API.Console.xdebug(message); 123 | } 124 | /// 125 | /// Performs a callback in V8 environment 126 | /// 127 | /// 128 | public void doCallback(string id) 129 | { 130 | Callback.Execute(id); 131 | } 132 | 133 | /// 134 | /// Fires an event in the page object (V8 runtime) passing some arguments 135 | /// 136 | /// 137 | /// 138 | /// 139 | public object fireEvent(string nickname, string jsonArgs) { 140 | return page._fireEvent(nickname, jsonArgs); 141 | } 142 | 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Docs/IC16039.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/IC16039.gif -------------------------------------------------------------------------------- /Docs/Upgrade.to.VS2010/Upgrade.To.VS2010.ERROR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/Upgrade.to.VS2010/Upgrade.To.VS2010.ERROR.png -------------------------------------------------------------------------------- /Docs/Upgrade.to.VS2010/Upgrade.To.VS2010.STEP1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/Upgrade.to.VS2010/Upgrade.To.VS2010.STEP1.png -------------------------------------------------------------------------------- /Docs/Upgrade.to.VS2010/Upgrade.To.VS2010.STEP2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/Upgrade.to.VS2010/Upgrade.To.VS2010.STEP2.png -------------------------------------------------------------------------------- /Docs/Upgrade.to.VS2010/Upgrade.To.VS2010.STEP3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/Upgrade.to.VS2010/Upgrade.To.VS2010.STEP3.png -------------------------------------------------------------------------------- /Docs/What is TrifleJS.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/What is TrifleJS.doc -------------------------------------------------------------------------------- /Docs/What.Is.Trifle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/What.Is.Trifle.png -------------------------------------------------------------------------------- /Docs/logo-150x150px.noletters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/logo-150x150px.noletters.png -------------------------------------------------------------------------------- /Docs/logo-200x200px.noletters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/logo-200x200px.noletters.png -------------------------------------------------------------------------------- /Docs/logo-260x260px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/logo-260x260px.png -------------------------------------------------------------------------------- /Docs/new.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/new.snk -------------------------------------------------------------------------------- /Docs/whatbrowser.org.IE8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Docs/whatbrowser.org.IE8.png -------------------------------------------------------------------------------- /Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Drawing; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Windows.Forms; 7 | using System.Text; 8 | 9 | namespace TrifleJS 10 | { 11 | public static class Extensions 12 | { 13 | /// 14 | /// Returns a unix timestamp 15 | /// 16 | /// 17 | public static int ToUnixTimestamp(this DateTime date) { 18 | if (date >= new DateTime(1970, 1, 1, 0, 0, 0, 0)) 19 | { 20 | TimeSpan span = (date - new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime()); 21 | return (int)span.TotalSeconds; 22 | } 23 | return -1; 24 | } 25 | 26 | /// 27 | /// Gets an entry in a dictionary by specifying its type 28 | /// 29 | /// 30 | /// 31 | /// 32 | /// 33 | public static T Get(this Dictionary dictionary, string key) 34 | { 35 | try 36 | { 37 | if (dictionary.ContainsKey(key)) { 38 | return (T)Convert.ChangeType(dictionary[key], typeof(T)); 39 | } 40 | } 41 | catch { } 42 | return default(T); 43 | } 44 | 45 | /// 46 | /// Gets an entry in a dictionary as an object 47 | /// 48 | /// 49 | /// 50 | /// 51 | public static object Get(this Dictionary dictionary, string key) 52 | { 53 | if (dictionary.ContainsKey(key)) 54 | { 55 | return dictionary[key]; 56 | } 57 | return null; 58 | } 59 | 60 | /// 61 | /// Gets all ancestor frames in a HtmlWindow 62 | /// 63 | /// 64 | /// 65 | public static List GetAllFrames(this HtmlWindow window) { 66 | List ancestors = new List {window}; 67 | bool added; int count = 0; 68 | do 69 | { 70 | // Keep recursing until we no longer 71 | // have anything else to add. 72 | added = false; 73 | foreach (HtmlWindow ancestor in ancestors.ToArray()) 74 | { 75 | foreach (HtmlWindow frame in ancestor.Frames) 76 | { 77 | if (!ancestors.Contains(frame)) 78 | { 79 | ancestors.Add(frame); 80 | added = true; 81 | count++; 82 | } 83 | } 84 | } 85 | } while (added && count < 100); // Limit to 100 frames 86 | return ancestors; 87 | } 88 | 89 | /// 90 | /// Gets a DOM Element at specific X/Y coordinates. 91 | /// Returns null if element not found. 92 | /// 93 | /// 94 | /// 95 | /// 96 | public static HtmlElement GetElementFromPoint(this HtmlDocument document, object x, object y) 97 | { 98 | try 99 | { 100 | if (x != null && y != null) 101 | { 102 | Point point = new Point((int)x, (int)y); 103 | return document.GetElementFromPoint(point); 104 | } 105 | } 106 | catch { } 107 | return null; 108 | } 109 | 110 | /// 111 | /// Gets a list of DOM elements using a CSS query selector. 112 | /// Only accepts simple selectors. (ie '.myclass' or '#myid' or 'mytag') 113 | /// @see http://stackoverflow.com/questions/11271737/how-do-you-click-a-button-in-a-webbrowser-control 114 | /// 115 | /// 116 | /// 117 | /// 118 | public static List GetElementFromSelector(this HtmlDocument document, string selector) 119 | { 120 | List output = new List(); 121 | if (!String.IsNullOrEmpty(selector)) 122 | { 123 | selector = selector.Trim(); 124 | if (selector.StartsWith("#")) 125 | { 126 | HtmlElement result = document.GetElementById(selector.Remove(0, 1)); 127 | if (result != null) output.Add(result); 128 | } 129 | else 130 | { 131 | if (selector.StartsWith(".")) 132 | { 133 | foreach (HtmlElement element in document.All) 134 | { 135 | 136 | if (element.GetAttribute("className") == selector.Remove(1)) 137 | output.Add(element); 138 | } 139 | } 140 | else 141 | { 142 | foreach (HtmlElement element in document.GetElementsByTagName(selector)) 143 | output.Add(element); 144 | } 145 | } 146 | } 147 | return output; 148 | } 149 | 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /LICENSE.MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Steven de Salas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Libs/Interop.IWshRuntimeLibrary.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Libs/Interop.IWshRuntimeLibrary.dll -------------------------------------------------------------------------------- /Libs/Interop.SHDocVw.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Libs/Interop.SHDocVw.dll -------------------------------------------------------------------------------- /Libs/Microsoft.VC90.CRT.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | S83+LBs1RkUxSkzia1WysaAhLbk= cKyCmIKF+fcGn6qaBhKuun+wAcQ= r+4y/NnOFgaANxNXoHL1jF95DUg= 6 | -------------------------------------------------------------------------------- /Libs/Microsoft.mshtml.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Libs/Microsoft.mshtml.dll -------------------------------------------------------------------------------- /Libs/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Libs/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /Libs/Newtonsoft.Json.dll.LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2007 James Newton-King 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the "Software"), to deal in the Software without 7 | restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or 9 | sell copies of the Software, and to permit persons to whom 10 | the Software is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | -------------------------------------------------------------------------------- /Libs/Noesis.Javascript.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Libs/Noesis.Javascript.dll -------------------------------------------------------------------------------- /Libs/Noesis.Javascript.dll.LICENSE: -------------------------------------------------------------------------------- 1 | 2 | ============================================ 3 | Javascript.NET 4 | @see http://javascriptdotnet.codeplex.com/license 5 | ============================================ 6 | 7 | New BSD License (BSD) 8 | Copyright (c) 2010, Noesis Innovation 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with 12 | or without modification, are permitted provided that the 13 | following conditions are met: 14 | 15 | * Redistributions of source code must retain the above 16 | copyright notice, this list of conditions and the following 17 | disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above 20 | copyright notice, this list of conditions and the following 21 | disclaimer in the documentation and/or other materials 22 | provided with the distribution. 23 | 24 | * Neither the name of Noesis Innovation nor the names of 25 | its contributors may be used to endorse or promote 26 | products derived from this software without specific 27 | prior written permission. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 30 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 31 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 32 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 33 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 34 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 36 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 37 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 39 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 40 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 41 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 | SUCH DAMAGE. 43 | 44 | 45 | ============================================ 46 | V8 JavaScript Engine 47 | @see https://github.com/v8/v8/blob/master/LICENSE.v8 48 | ============================================ 49 | 50 | 51 | Copyright 2006-2012, the V8 project authors. All rights reserved. 52 | Redistribution and use in source and binary forms, with or without 53 | modification, are permitted provided that the following conditions are 54 | met: 55 | 56 | * Redistributions of source code must retain the above copyright 57 | notice, this list of conditions and the following disclaimer. 58 | * Redistributions in binary form must reproduce the above 59 | copyright notice, this list of conditions and the following 60 | disclaimer in the documentation and/or other materials provided 61 | with the distribution. 62 | * Neither the name of Google Inc. nor the names of its 63 | contributors may be used to endorse or promote products derived 64 | from this software without specific prior written permission. 65 | 66 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 67 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 68 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 69 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 70 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 71 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 72 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 73 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 74 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 75 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 76 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 77 | 78 | 79 | ============================================ 80 | Strongtalk 81 | @see https://github.com/v8/v8/blob/master/LICENSE.strongtalk 82 | ============================================ 83 | 84 | 85 | Copyright (c) 1994-2006 Sun Microsystems Inc. 86 | All Rights Reserved. 87 | 88 | Redistribution and use in source and binary forms, with or without 89 | modification, are permitted provided that the following conditions are 90 | met: 91 | 92 | - Redistributions of source code must retain the above copyright notice, 93 | this list of conditions and the following disclaimer. 94 | 95 | - Redistribution in binary form must reproduce the above copyright 96 | notice, this list of conditions and the following disclaimer in the 97 | documentation and/or other materials provided with the distribution. 98 | 99 | - Neither the name of Sun Microsystems or the names of contributors may 100 | be used to endorse or promote products derived from this software without 101 | specific prior written permission. 102 | 103 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 104 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 105 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 106 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 107 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 108 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 109 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 110 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 111 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 112 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 113 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 114 | 115 | 116 | 117 | ============================================ 118 | Valgrind 119 | @see https://github.com/v8/v8/blob/master/LICENSE.valgrind 120 | ============================================ 121 | 122 | 123 | This file is part of Valgrind, a dynamic binary instrumentation 124 | framework. 125 | 126 | Copyright (C) 2000-2007 Julian Seward. All rights reserved. 127 | 128 | Redistribution and use in source and binary forms, with or without 129 | modification, are permitted provided that the following conditions 130 | are met: 131 | 132 | 1. Redistributions of source code must retain the above copyright 133 | notice, this list of conditions and the following disclaimer. 134 | 135 | 2. The origin of this software must not be misrepresented; you must 136 | not claim that you wrote the original software. If you use this 137 | software in a product, an acknowledgment in the product 138 | documentation would be appreciated but is not required. 139 | 140 | 3. Altered source versions must be plainly marked as such, and must 141 | not be misrepresented as being the original software. 142 | 143 | 4. The name of the author may not be used to endorse or promote 144 | products derived from this software without specific prior written 145 | permission. 146 | 147 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 148 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 149 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 150 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 151 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 152 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 153 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 154 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 155 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 156 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 157 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /Libs/msvcm90.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Libs/msvcm90.dll -------------------------------------------------------------------------------- /Libs/msvcp90.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Libs/msvcp90.dll -------------------------------------------------------------------------------- /Libs/msvcr90.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/Libs/msvcr90.dll -------------------------------------------------------------------------------- /Native/COM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Windows.Forms; 4 | using mshtml; 5 | 6 | namespace TrifleJS.Native 7 | { 8 | public class Interface 9 | { 10 | [Guid("6D5140C1-7436-11CE-8034-00AA006009FA")] 11 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 12 | [ComImport] 13 | public interface UCOMIServiceProvider 14 | { 15 | [return: MarshalAs(UnmanagedType.I4)] 16 | [PreserveSig] 17 | int QueryService( 18 | [In] ref Guid guidService, 19 | [In] ref Guid riid, 20 | [Out] out IntPtr ppvObject); 21 | } 22 | [ComImport()] 23 | [ComVisible(true)] 24 | [Guid("79eac9d5-bafa-11ce-8c82-00aa004ba90b")] 25 | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] 26 | public interface IWindowForBindingUI 27 | { 28 | [return: MarshalAs(UnmanagedType.I4)] 29 | [PreserveSig] 30 | int GetWindow( 31 | [In] ref Guid rguidReason, 32 | [In, Out] ref IntPtr phwnd); 33 | } 34 | [ComImport()] 35 | [ComVisible(true)] 36 | [Guid("79eac9d7-bafa-11ce-8c82-00aa004ba90b")] 37 | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] 38 | public interface IHttpSecurity 39 | { 40 | //derived from IWindowForBindingUI 41 | [return: MarshalAs(UnmanagedType.I4)] 42 | [PreserveSig] 43 | int GetWindow( 44 | [In] ref Guid rguidReason, 45 | [In, Out] ref IntPtr phwnd); 46 | [PreserveSig] 47 | int OnSecurityProblem( 48 | [In, MarshalAs(UnmanagedType.U4)] uint dwProblem); 49 | } 50 | [ComImport, ComVisible(true)] 51 | [Guid("C4D244B0-D43E-11CF-893B-00AA00BDCE1A")] 52 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 53 | public interface IDocHostShowUI 54 | { 55 | //MIDL_INTERFACE("c4d244b0-d43e-11cf-893b-00aa00bdce1a") 56 | //IDocHostShowUI : public IUnknown 57 | //{ 58 | //public: 59 | // virtual HRESULT STDMETHODCALLTYPE ShowMessage( 60 | // /* [in] */ HWND hwnd, 61 | // /* [in] */ LPOLESTR lpstrText, 62 | // /* [in] */ LPOLESTR lpstrCaption, 63 | // /* [in] */ DWORD dwType, 64 | // /* [in] */ LPOLESTR lpstrHelpFile, 65 | // /* [in] */ DWORD dwHelpContext, 66 | // /* [out] */ LRESULT *plResult) = 0; 67 | 68 | // virtual HRESULT STDMETHODCALLTYPE ShowHelp( 69 | // /* [in] */ HWND hwnd, 70 | // /* [in] */ LPOLESTR pszHelpFile, 71 | // /* [in] */ UINT uCommand, 72 | // /* [in] */ DWORD dwData, 73 | // /* [in] */ POINT ptMouse, 74 | // /* [out] */ IDispatch *pDispatchObjectHit) = 0; 75 | 76 | //}; 77 | [return: MarshalAs(UnmanagedType.I4)] 78 | [PreserveSig] 79 | int ShowMessage( 80 | IntPtr hwnd, 81 | [MarshalAs(UnmanagedType.LPWStr)] string lpstrText, 82 | [MarshalAs(UnmanagedType.LPWStr)] string lpstrCaption, 83 | [MarshalAs(UnmanagedType.U4)] uint dwType, 84 | [MarshalAs(UnmanagedType.LPWStr)] string lpstrHelpFile, 85 | [MarshalAs(UnmanagedType.U4)] uint dwHelpContext, 86 | [In, Out] ref int lpResult); 87 | 88 | [return: MarshalAs(UnmanagedType.I4)] 89 | [PreserveSig] 90 | int ShowHelp( 91 | IntPtr hwnd, 92 | [MarshalAs(UnmanagedType.LPWStr)] string pszHelpFile, 93 | [MarshalAs(UnmanagedType.U4)] uint uCommand, 94 | [MarshalAs(UnmanagedType.U4)] uint dwData, 95 | [In, MarshalAs(UnmanagedType.Struct)] tagPOINT ptMouse, 96 | [Out, MarshalAs(UnmanagedType.IDispatch)] object pDispatchObjectHit); 97 | } 98 | } 99 | 100 | public sealed class HResult 101 | { 102 | internal const int INET_E_DEFAULT_ACTION = unchecked((int)0x800C0011); 103 | internal const int E_NOINTERFACE = unchecked((int)0x80004002); 104 | internal const int RPC_E_RETRY = unchecked((int)0x80010109); 105 | internal const int S_FALSE = 1; 106 | internal const int S_OK = unchecked((int)0x00000000); 107 | internal const int E_NOTIMPL = unchecked((int)0x80004001); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Native/IgnoreSSLErrorBrowser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Windows.Forms; 4 | using System.Collections.Generic; 5 | using mshtml; 6 | 7 | namespace TrifleJS.Native 8 | { 9 | /// 10 | /// This class enhances the WebBrowser control by automaticaly ignoring any SSL certificate errors 11 | /// @see http://jiangsheng.net/2013/07/17/howto-ignoring-web-browser-certificate-errors-in-webbrowser-host/ 12 | /// 13 | public class IgnoreSSLBrowser : Browser 14 | { 15 | internal static Guid IID_IHttpSecurity = new Guid("79eac9d7-bafa-11ce-8c82-00aa004ba90b"); 16 | internal static Guid IID_IWindowForBindingUI = new Guid("79eac9d5-bafa-11ce-8c82-00aa004ba90b"); 17 | 18 | #region IgnoreSSLErrorBrowserSite 19 | 20 | protected override System.Windows.Forms.WebBrowserSiteBase CreateWebBrowserSiteBase() 21 | { 22 | return new IgnoreSSLErrorBrowserSite(this); 23 | } 24 | 25 | protected class IgnoreSSLErrorBrowserSite : WebBrowserSite, Interface.UCOMIServiceProvider, Interface.IHttpSecurity, Interface.IWindowForBindingUI 26 | { 27 | private IgnoreSSLBrowser host; 28 | 29 | public IgnoreSSLErrorBrowserSite(IgnoreSSLBrowser host) 30 | : base(host) 31 | { 32 | this.host = host; 33 | } 34 | 35 | #region IServiceProvider Members 36 | 37 | public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject) 38 | { 39 | if (riid == IID_IHttpSecurity) 40 | { 41 | ppvObject = Marshal.GetComInterfaceForObject(this, typeof(Interface.IHttpSecurity)); 42 | return HResult.S_OK; 43 | } 44 | if (riid == IID_IWindowForBindingUI) 45 | { 46 | ppvObject = Marshal.GetComInterfaceForObject(this, typeof(Interface.IWindowForBindingUI)); 47 | return HResult.S_OK; 48 | } 49 | ppvObject = IntPtr.Zero; 50 | return HResult.E_NOINTERFACE; 51 | } 52 | 53 | #endregion 54 | 55 | #region IHttpSecurity 56 | 57 | public int GetWindow(ref Guid rguidReason, ref IntPtr phwnd) 58 | { 59 | if (rguidReason == IID_IHttpSecurity 60 | || rguidReason == IID_IWindowForBindingUI) 61 | { 62 | phwnd = this.host.Handle; 63 | return HResult.S_OK; 64 | } 65 | else 66 | { 67 | phwnd = IntPtr.Zero; 68 | return HResult.S_FALSE; 69 | } 70 | } 71 | 72 | public int OnSecurityProblem(uint dwProblem) 73 | { 74 | if (IgnoreSSLErrors) 75 | { 76 | return HResult.S_OK; 77 | } 78 | else 79 | { 80 | return HResult.S_FALSE; 81 | } 82 | } 83 | 84 | #endregion 85 | 86 | } 87 | 88 | #endregion 89 | 90 | } 91 | 92 | 93 | 94 | } -------------------------------------------------------------------------------- /Native/SkipDialogBrowser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Forms; 4 | using System.Text; 5 | using mshtml; 6 | 7 | namespace TrifleJS.Native 8 | { 9 | public class SkipDialogBrowser: IgnoreSSLBrowser 10 | { 11 | #region OnShowMessage 12 | 13 | public enum ResultId 14 | { 15 | Ok = 1, 16 | Cancel = 2, 17 | Abort = 3, 18 | Retry = 4, 19 | Ignore = 5, 20 | Yes = 6, 21 | No = 7, 22 | TryAgain = 10, 23 | Continue = 11 24 | } 25 | 26 | public class ShowMessageEventArgs : EventArgs 27 | { 28 | public ShowMessageEventArgs(string text, string caption, uint type, string helpFile, uint helpContext) 29 | { 30 | Text = text; 31 | Caption = caption; 32 | Type = type; 33 | HelpFile = helpFile; 34 | HelpContext = helpContext; 35 | Handled = true; // Default to Handled 36 | Result = ResultId.Ok; // Default to OK (IDOK = 1) @see https://msdn.microsoft.com/en-us/library/ms645505(v=vs.85).aspx 37 | } 38 | 39 | public bool Handled { get; set; } 40 | public ResultId Result { get; set; } 41 | public uint Type { get; private set; } 42 | public uint HelpContext { get; private set; } 43 | public string Text { get; private set; } 44 | public string Caption { get; private set; } 45 | public string HelpFile { get; private set; } 46 | } 47 | 48 | protected virtual void OnShowMessage(ShowMessageEventArgs e) 49 | { 50 | var handler = this.Events["ShowMessage"] as EventHandler; 51 | 52 | if (handler != null) 53 | { 54 | handler(this, e); 55 | } 56 | } 57 | 58 | public event EventHandler ShowMessage 59 | { 60 | add { this.Events.AddHandler("ShowMessage", value); } 61 | remove { this.Events.RemoveHandler("ShowMessage", value); } 62 | } 63 | 64 | #endregion 65 | 66 | #region EnhancedWebBrowserSite 67 | 68 | protected override System.Windows.Forms.WebBrowserSiteBase CreateWebBrowserSiteBase() 69 | { 70 | return new SkipDialogBrowserSite(this); 71 | } 72 | 73 | protected class SkipDialogBrowserSite : IgnoreSSLErrorBrowserSite, Interface.IDocHostShowUI 74 | { 75 | private SkipDialogBrowser host; 76 | 77 | public SkipDialogBrowserSite(SkipDialogBrowser host) 78 | : base(host) 79 | { 80 | this.host = host; 81 | } 82 | 83 | #region IDocHostShowUI Members 84 | 85 | int Interface.IDocHostShowUI.ShowMessage(IntPtr hwnd, string lpstrText, 86 | string lpstrCaption, uint dwType, 87 | string lpstrHelpFile, uint dwHelpContext, ref int lpResult) 88 | { 89 | //Initially, lpResult is set 0 //S_OK 90 | API.Console.warn("ShowMessage: " + lpstrText); 91 | //Host did not display its UI. MSHTML displays its message box. 92 | var e = new ShowMessageEventArgs(lpstrText, lpstrCaption, dwType, lpstrHelpFile, dwHelpContext); 93 | this.host.OnShowMessage(e); 94 | 95 | lpResult = (e.Handled) ? (int)e.Result : 0; 96 | 97 | return HResult.S_OK; 98 | } 99 | 100 | int Interface.IDocHostShowUI.ShowHelp(IntPtr hwnd, string pszHelpFile, uint uCommand, uint dwData, tagPOINT ptMouse, object pDispatchObjectHit) 101 | { 102 | return HResult.E_NOTIMPL; 103 | } 104 | 105 | #endregion 106 | 107 | } 108 | 109 | #endregion 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TrifleJS")] 9 | [assembly: AssemblyDescription("A headless Internet Explorer browser with a Javascript API. Port of PhantomJS")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyProduct("http://triflejs.org")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | 15 | // Setting ComVisible to false makes the types in this assembly not visible 16 | // to COM components. If you need to access a type in this assembly from 17 | // COM, set the ComVisible attribute to true on that type. 18 | [assembly: ComVisible(false)] 19 | 20 | // The following GUID is for the ID of the typelib if this project is exposed to COM 21 | [assembly: Guid("c759e0c0-9739-4872-9490-808729fb0a06")] 22 | 23 | // Version information for an assembly consists of the following four values: 24 | // 25 | // Major Version 26 | // Minor Version 27 | // Build Number 28 | // Revision 29 | // 30 | // You can specify all the values or you can default the Build and Revision Numbers 31 | // by using the '*' as shown below: 32 | // [assembly: AssemblyVersion("1.0.*")] 33 | [assembly: AssemblyVersion("1.0.0.0")] 34 | [assembly: AssemblyFileVersion("1.0.0.0")] 35 | [assembly: AssemblyCompanyAttribute("")] 36 | [assembly: AssemblyCopyrightAttribute("Steven de Salas (MIT License)")] 37 | -------------------------------------------------------------------------------- /Proxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TrifleJS 6 | { 7 | /// 8 | /// Static class used for tracking and modifying proxy information. 9 | /// Proxy information used by IE is held in the registry. 10 | /// 11 | public static class Proxy 12 | { 13 | /// 14 | /// Default settings path for IE 15 | /// 16 | public static string defaultIESettingsPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"; 17 | 18 | /// 19 | /// Proxy server information (ie "192.168.0.255:8888") 20 | /// 21 | public static string server = null; 22 | 23 | /// 24 | /// Proxy authentication (ie "jsmith:passwd") 25 | /// 26 | public static string auth = null; 27 | 28 | /// 29 | /// Type of proxy server (ie [http|socks5|none) 30 | /// 31 | public static string type = "http"; 32 | 33 | /// 34 | /// Tracks wether the proxy has been set inside the app or not 35 | /// 36 | public static bool isSet = false; 37 | 38 | /// 39 | /// Sets the IE Proxy at windows level 40 | /// 41 | public static void Set() { 42 | Backup.Save(); 43 | if (type == "none") 44 | { 45 | Utils.TryWriteRegistryKey(defaultIESettingsPath, "ProxyEnable", 0, Microsoft.Win32.RegistryValueKind.DWord); 46 | } 47 | else { 48 | Utils.TryWriteRegistryKey(defaultIESettingsPath, "ProxyEnable", 1, Microsoft.Win32.RegistryValueKind.DWord); 49 | Utils.TryWriteRegistryKey(defaultIESettingsPath, "ProxyServer", GetParsedServerInfo(), Microsoft.Win32.RegistryValueKind.String); 50 | Utils.TryWriteRegistryKey(defaultIESettingsPath, "ProxyOverride", "", Microsoft.Win32.RegistryValueKind.String); 51 | } 52 | Proxy.isSet = true; 53 | } 54 | 55 | private static string GetParsedServerInfo() 56 | { 57 | switch (type) 58 | { 59 | case "http": 60 | // Include 'http' & 'https' by simply using the host:port configuration 61 | return server; 62 | case "socks5": 63 | // Limit connection to socks only 64 | return String.Format("socks={0}", server); 65 | case "none": 66 | // Blank server 67 | return ""; 68 | default: 69 | Console.Error.WriteLine(String.Format("Incorrect --proxy-type option: \"{0}\"", type)); 70 | return ""; 71 | } 72 | } 73 | 74 | /// 75 | /// Gets the proxy username 76 | /// 77 | public static string Username { 78 | get { 79 | if (auth == null) { 80 | return null; 81 | }; 82 | return auth.Split(':')[0]; 83 | } 84 | } 85 | 86 | /// 87 | /// Gets the proxy password 88 | /// 89 | public static string Password { 90 | get { 91 | if (auth == null || !auth.Contains(":")) { 92 | return null; 93 | } 94 | return auth.Split(':')[1]; 95 | } 96 | } 97 | 98 | /// 99 | /// Encapsulates functionality for backing up and restoring existing proxy information 100 | /// 101 | public static class Backup 102 | { 103 | /// 104 | /// Saves the existing proxy information 105 | /// 106 | public static void Save() { 107 | MigrateProxy = Utils.TryReadRegistryKey(defaultIESettingsPath, "MigrateProxy"); 108 | ProxyEnable = Utils.TryReadRegistryKey(defaultIESettingsPath, "ProxyEnable"); 109 | ProxyServer = Utils.TryReadRegistryKey(defaultIESettingsPath, "ProxyServer"); 110 | ProxyOverride = Utils.TryReadRegistryKey(defaultIESettingsPath, "ProxyOverride"); 111 | } 112 | /// 113 | /// Restores previous proxy information 114 | /// 115 | public static void Restore() { 116 | if (Proxy.isSet) 117 | { 118 | Utils.TryWriteRegistryKey(defaultIESettingsPath, "MigrateProxy", MigrateProxy, Microsoft.Win32.RegistryValueKind.DWord); 119 | Utils.TryWriteRegistryKey(defaultIESettingsPath, "ProxyEnable", ProxyEnable, Microsoft.Win32.RegistryValueKind.DWord); 120 | Utils.TryWriteRegistryKey(defaultIESettingsPath, "ProxyServer", ProxyServer, Microsoft.Win32.RegistryValueKind.String); 121 | Utils.TryWriteRegistryKey(defaultIESettingsPath, "ProxyOverride", ProxyOverride, Microsoft.Win32.RegistryValueKind.String); 122 | } 123 | } 124 | public static object MigrateProxy; 125 | public static object ProxyEnable; 126 | public static object ProxyServer; 127 | public static object ProxyOverride; 128 | } 129 | 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![trifjeJS](https://raw.github.com/sdesalas/trifleJS/master/Docs/logo-260x260px.png "trifleJS") 2 | 3 | A headless Internet Explorer browser using the [.NET WebBrowser Class](http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.aspx) with a Javascript API running on the [V8 JavaScript Engine](http://en.wikipedia.org/wiki/V8_(JavaScript_engine)). 4 | 5 | The API is coded as a port of [PhantomJS](http://phantomjs.org). Basically, if you have used phantom before then you already know how to use TrifleJS. 6 | 7 | ![What is TrifleJS?](https://raw.github.com/sdesalas/trifleJS/master/Docs/What.Is.Trifle.png "What is TrifleJS?") 8 | 9 | It supports different version of IE interchangeably depending on the current version installed (IE9 can emulate IE7, IE8 or IE9 but not IE10). 10 | 11 | C:\> TrifleJS.exe --emulate=IE8 --render=http://whatbrowser.org/ 12 | 13 | ![IE 8](https://raw.github.com/sdesalas/trifleJS/master/Docs/whatbrowser.org.IE8.png "Running as IE 8") 14 | 15 | The following is a list of features that have been ported from PhantomJS. 16 | 17 | We are targetting version 1.7 as webdriver support (added in v 1.8) is too much work to put in at this stage and provides only marginal benefit. 18 | 19 | ### API Implementation 20 | 21 | We are a bit over two-thirds through the [PhantomJS API](http://phantomjs.org/api/) at `v1.7`. 22 | 23 | - [API Status (77%)](http://triflejs.org#post-112) 24 | 25 | More information on each component is available on [triflejs.org](http://triflejs.org): 26 | 27 | - [Command Line Options](http://triflejs.org#post-29) 28 | - [Global Methods](http://triflejs.org#post-11) 29 | - [Object: phantom](http://triflejs.org#post-18) 30 | - [Module: System](http://triflejs.org#post-24) 31 | - [Module: FileSystem](http://triflejs.org#post-27) 32 | - [Module: WebPage](http://triflejs.org#post-20) 33 | - [Module: WebServer](http://triflejs.org#post-63) 34 | - [Module: ChildProcess](http://triflejs.org#post-222) 35 | - [New Features](http://triflejs.org/#post-31) 36 | 37 | Some of the big ticket items currently missing from the automation are: 38 | 39 | - IE Windows (File Upload, SSL Certificate Error) 40 | - Mouse / Keyboard interaction 41 | - ChildProcess module 42 | 43 | Some items that are being deliberately left out: 44 | 45 | - Support for WebDriver 46 | 47 | ### Roadmap 48 | 49 | - `v0.3` - 56% of PhantomJS API ([Release Notes](https://github.com/sdesalas/trifleJS/releases/tag/v0.3)) 50 | - `v0.4` - 72% of PhantomJS API ([Release Notes](https://github.com/sdesalas/trifleJS/releases/tag/v0.4)) 51 | - `v0.5` - (work in progress..) 52 | - `v0.6` - 100% of PhantomJS non-WebPage modules, 80% of WebPage module API 53 | - `v0.7` - 100% of PhantomJS Core API (v1.7) + internal unit tests 54 | - `v0.8` - [CasperJS](https://github.com/n1k0/casperjs) Support (implement Test suite and fixes) 55 | - `v0.9` - Testing and Support for Windows platforms (after XP SP2). 56 | - `v1.0` - Only minor Bugfixes left 57 | - `v1.1` - Nice to haves (WebDriver, improved IPC, REPL Autocompletion etc) 58 | 59 | ### Download 60 | 61 | This code is still very much in beta. Check again for updates. 62 | 63 | - [Stable `v0.4`](https://github.com/sdesalas/trifleJS/releases/download/v0.4/TrifleJS.zip) 64 | - [Latest Beta `v0.5`](https://github.com/sdesalas/trifleJS/raw/master/Build/Binary/TrifleJS.Latest.zip) 65 | 66 | If you are not sure which version to use then download the ‘Latest Stable’ version. 67 | 68 | #### System Requirements 69 | 70 | The ideal installation is Windows 7 (with .NET 4.0 or higher) using IE11, however this will still work in 32-bit XP (with SP2) or versions of the [Windows NT Kernel](http://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions#Windows_NT) released after 2001. 71 | 72 | Here is a breakdown of current unit tests: 73 | 74 | |Windows Version | IE 8 | IE 9 | IE 10 | IE 11 | EDGE 12 | 75 | |-------------------|-------|-------|-------|-------|---------| 76 | |XP (SP2) | Broken| N / A | N / A | N / A | N / A | 77 | |Vista 32/64bit | | | N / A | N / A | N / A | 78 | |Ser 2003 32/64bi | | | N / A | N / A | N / A | 79 | |Win7 32bit | 100% | 100% | 100% | 100% | N / A | 80 | |Win7 64bit | 100% | 100% | 100% | 100% | N / A | 81 | |2008R2 64bit | | | | | N / A | 82 | |Win8 32bit | | | | | N / A | 83 | |Win8 64bit | | | | | N / A | 84 | |2012R2 64bit | 100% | 100% | 100% | 100% | N / A | 85 | |Win10 32bit | | | | | | 86 | |Win10 64bit | | | | | | 87 | |Ser 2016 64bit | | | | | | 88 | 89 | Key: 90 | 91 | - 100%: Passing all unit tests 92 | - 1-99%: Passing some unit tests 93 | - Broken: Build broken. 94 | - (blank): Untested 95 | - N / A: IE Version unavailable in this platform 96 | 97 | -------------------------------------------------------------------------------- /TrifleJS.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual C# Express 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrifleJS", "TrifleJS.csproj", "{6130407F-322B-4E41-AFDB-FCF1C17D6C19}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {6130407F-322B-4E41-AFDB-FCF1C17D6C19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {6130407F-322B-4E41-AFDB-FCF1C17D6C19}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {6130407F-322B-4E41-AFDB-FCF1C17D6C19}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {6130407F-322B-4E41-AFDB-FCF1C17D6C19}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using System.Text.RegularExpressions; 6 | using Newtonsoft.Json; 7 | using Microsoft.Win32; 8 | using System.Text; 9 | 10 | namespace TrifleJS 11 | { 12 | /// 13 | /// Various tools to make life easier 14 | /// 15 | public class Utils 16 | { 17 | /// 18 | /// Debug message to console 19 | /// 20 | /// 21 | /// 22 | public static void Debug(object message, params object[] args) 23 | { 24 | if (Program.Verbose) 25 | { 26 | ConsoleColor normalColor = Console.ForegroundColor; 27 | Console.ForegroundColor = ConsoleColor.DarkMagenta; 28 | if (args != null) 29 | { 30 | Console.WriteLine(String.Format(message as string, args)); 31 | } 32 | else 33 | { 34 | Console.WriteLine(message); 35 | } 36 | Console.ForegroundColor = normalColor; 37 | } 38 | } 39 | 40 | /// 41 | /// Tries reading a registry key for the current user, returns null if not found 42 | /// 43 | /// 44 | /// 45 | /// 46 | public static object TryReadRegistryKey(string path, string name) 47 | { 48 | return TryReadRegistryKey(Registry.CurrentUser, path, name); 49 | } 50 | 51 | /// 52 | /// Tries reading a registry key using a specific path, returns null if not found 53 | /// 54 | /// 55 | /// 56 | /// 57 | public static object TryReadRegistryKey(RegistryKey root, string path, string name) 58 | { 59 | try 60 | { 61 | // Use current user, no need for admin permissions 62 | RegistryKey key = root.OpenSubKey(path); 63 | if (key != null) 64 | { 65 | return key.GetValue(name); 66 | } 67 | } 68 | catch { } 69 | return null; 70 | } 71 | 72 | /// 73 | /// Tries to write a key to registry catching any exceptions, returns true if the key was written successfully 74 | /// 75 | /// 76 | /// 77 | /// 78 | /// 79 | /// 80 | public static bool TryWriteRegistryKey(string path, string name, object value, RegistryValueKind kind) 81 | { 82 | try 83 | { 84 | // Use current user, no need for admin permissions 85 | RegistryKey key = Registry.CurrentUser.OpenSubKey(path, true); 86 | if (key == null) 87 | { 88 | key = CreateRegistryPath(Registry.CurrentUser, path); 89 | } 90 | key.SetValue(name, value, kind); 91 | return true; 92 | } 93 | catch 94 | { 95 | return false; 96 | } 97 | } 98 | 99 | /// 100 | /// Creates a path in the registry, including all necessary parent paths 101 | /// 102 | /// 103 | /// 104 | /// 105 | private static RegistryKey CreateRegistryPath(RegistryKey key, string path) 106 | { 107 | foreach (string name in path.Split('\\')) 108 | { 109 | RegistryKey subkey = key.OpenSubKey(name, true); 110 | if (subkey == null) 111 | { 112 | key.CreateSubKey(name); 113 | subkey = key.OpenSubKey(name, true); 114 | } 115 | key = subkey; 116 | }; 117 | return key; 118 | } 119 | 120 | /// 121 | /// Converts a COM object to an array of objects 122 | /// 123 | /// 124 | /// 125 | public static object[] COMToArray(object comObject) 126 | { 127 | List output = new List(); 128 | int? length = (int?)(comObject.GetType()).InvokeMember("length", System.Reflection.BindingFlags.GetProperty, null, comObject, null); 129 | for (int i = 0; i < length; i++) 130 | { 131 | output.Add(comObject.GetType().InvokeMember(String.Format("{0}", i), BindingFlags.GetProperty, null, comObject, null)); 132 | } 133 | return output.ToArray(); 134 | } 135 | 136 | /// 137 | /// Converts a COM object to dictionary (useful for outputting error information) 138 | /// 139 | /// 140 | /// 141 | public static Dictionary COMToErrorObject(object comObject) 142 | { 143 | Dictionary output = new Dictionary(); 144 | PropertyInfo[] props = comObject.GetType().GetProperties(); 145 | foreach (PropertyInfo prop in props) 146 | { 147 | output.Add(prop.Name, comObject.GetType().InvokeMember(prop.Name, BindingFlags.GetProperty, null, comObject, null)); 148 | } 149 | return output; 150 | } 151 | 152 | /// 153 | /// Serialises an object to JSON string 154 | /// 155 | /// 156 | /// 157 | public static string Serialize(object obj) 158 | { 159 | return Newtonsoft.Json.JsonConvert.SerializeObject(obj, Formatting.Indented); 160 | } 161 | 162 | /// 163 | /// Deserialises a JSON string into an object 164 | /// 165 | /// 166 | /// 167 | public static object Deserialize(string json) 168 | { 169 | return Newtonsoft.Json.JsonConvert.DeserializeObject>(json); 170 | } 171 | 172 | /// 173 | /// Returns a unique ID string (16^8 = 4.3 trillion combinations) 174 | /// 175 | /// 176 | public static string NewUid() 177 | { 178 | return Guid.NewGuid().ToString().Substring(0, 8); 179 | } 180 | 181 | /// 182 | /// Note that windows uses "UTF-8" instead of "UTF8" 183 | /// so we have to sanitize these strings 184 | /// 185 | internal static Encoding GetEncoding(string encoding) 186 | { 187 | if (encoding != null) 188 | { 189 | if (encoding.IndexOf("utf", StringComparison.InvariantCultureIgnoreCase) == 0 190 | && !encoding.Contains("-")) 191 | { 192 | encoding = Regex.Replace(encoding, "utf", "UTF-", RegexOptions.IgnoreCase); 193 | } 194 | return Encoding.GetEncoding(encoding); 195 | } 196 | return Encoding.UTF8; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /examples/arguments.js: -------------------------------------------------------------------------------- 1 | var system = require('system'); 2 | if (system.args.length === 1) { 3 | console.log('Try to pass some args when invoking this script!'); 4 | } else { 5 | system.args.forEach(function (arg, i) { 6 | console.log(i + ': ' + arg); 7 | }); 8 | } 9 | phantom.exit(); 10 | -------------------------------------------------------------------------------- /examples/countdown.js: -------------------------------------------------------------------------------- 1 | var t = 10, 2 | interval = setInterval(function(){ 3 | if ( t > 0 ) { 4 | console.log(t--); 5 | } else { 6 | console.log("BLAST OFF!"); 7 | phantom.exit(); 8 | } 9 | }, 1000); 10 | -------------------------------------------------------------------------------- /examples/detectsniff.js: -------------------------------------------------------------------------------- 1 | // Detect if a web page sniffs the user agent or not. 2 | 3 | var page = require('webpage').create(), 4 | system = require('system'), 5 | sniffed, 6 | address; 7 | 8 | page.onInitialized = function () { 9 | page.evaluate(function () { 10 | 11 | (function () { 12 | var userAgent = window.navigator.userAgent, 13 | platform = window.navigator.platform; 14 | 15 | window.navigator = { 16 | appCodeName: 'Mozilla', 17 | appName: 'Netscape', 18 | cookieEnabled: false, 19 | sniffed: false 20 | }; 21 | 22 | window.navigator.__defineGetter__('userAgent', function () { 23 | window.navigator.sniffed = true; 24 | return userAgent; 25 | }); 26 | 27 | window.navigator.__defineGetter__('platform', function () { 28 | window.navigator.sniffed = true; 29 | return platform; 30 | }); 31 | })(); 32 | }); 33 | }; 34 | 35 | if (system.args.length === 1) { 36 | console.log('Usage: detectsniff.js '); 37 | phantom.exit(1); 38 | } else { 39 | address = system.args[1]; 40 | console.log('Checking ' + address + '...'); 41 | page.open(address, function (status) { 42 | if (status !== 'success') { 43 | console.log('FAIL to load the address'); 44 | phantom.exit(); 45 | } else { 46 | window.setTimeout(function () { 47 | sniffed = page.evaluate(function () { 48 | return navigator.sniffed; 49 | }); 50 | if (sniffed) { 51 | console.log('The page tried to sniff the user agent.'); 52 | } else { 53 | console.log('The page did not try to sniff the user agent.'); 54 | } 55 | phantom.exit(); 56 | }, 1500); 57 | } 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /examples/direction.js: -------------------------------------------------------------------------------- 1 | // Get driving direction using Google Directions API. 2 | 3 | var page = require('webpage').create(), 4 | system = require('system'), 5 | origin, dest, steps; 6 | 7 | if (system.args.length < 3) { 8 | console.log('Usage: direction.js origin destination'); 9 | console.log('Example: direction.js "San Diego" "Palo Alto"'); 10 | phantom.exit(1); 11 | } else { 12 | origin = system.args[1]; 13 | dest = system.args[2]; 14 | page.open(encodeURI('http://maps.googleapis.com/maps/api/directions/xml?origin=' + origin + 15 | '&destination=' + dest + '&units=imperial&mode=driving&sensor=false'), function (status) { 16 | if (status !== 'success') { 17 | console.log('Unable to access network'); 18 | } else { 19 | steps = page.plainText.match(/(.*)<\/html_instructions>/ig); 20 | if (steps == null) { 21 | console.log('No data available for ' + origin + ' to ' + dest); 22 | } else { 23 | steps.forEach(function (ins) { 24 | ins = ins.replace(/\</ig, '<').replace(/\>/ig, '>'); 25 | ins = ins.replace(/\
/g, ''); 27 | console.log(ins); 28 | }); 29 | console.log(''); 30 | console.log(page.plainText.match(/.*<\/copyrights>/ig).join('').replace(/<.*?>/g, '')); 31 | } 32 | } 33 | phantom.exit(); 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /examples/fibo.js: -------------------------------------------------------------------------------- 1 | var fibs = [0, 1]; 2 | var ticker = window.setInterval(function () { 3 | console.log(fibs[fibs.length - 1]); 4 | fibs.push(fibs[fibs.length - 1] + fibs[fibs.length - 2]); 5 | if (fibs.length > 10) { 6 | window.clearInterval(ticker); 7 | phantom.exit(); 8 | } 9 | }, 300); 10 | -------------------------------------------------------------------------------- /examples/follow.js: -------------------------------------------------------------------------------- 1 | // List following and followers from several accounts 2 | 3 | var users = ['PhantomJS', 4 | 'ariyahidayat', 5 | 'detronizator', 6 | 'KDABQt', 7 | 'lfranchi', 8 | 'jonleighton', 9 | '_jamesmgreene', 10 | 'Vitalliumm']; 11 | 12 | function follow(user, callback) { 13 | var page = require('webpage').create(); 14 | page.open('http://mobile.twitter.com/' + user, function (status) { 15 | if (status === 'fail') { 16 | console.log(user + ': ?'); 17 | } else { 18 | var data = page.evaluate(function () { 19 | return document.querySelector('div.profile td.stat.stat-last div.statnum').innerText; 20 | }); 21 | console.log(user + ': ' + data); 22 | } 23 | page.close(); 24 | callback.apply(); 25 | }); 26 | } 27 | 28 | function process() { 29 | if (users.length > 0) { 30 | var user = users[0]; 31 | users.splice(0, 1); 32 | follow(user, process); 33 | } else { 34 | phantom.exit(); 35 | } 36 | } 37 | 38 | process(); 39 | -------------------------------------------------------------------------------- /examples/hello.js: -------------------------------------------------------------------------------- 1 | console.log('Hello, world!'); 2 | phantom.exit(); 3 | -------------------------------------------------------------------------------- /examples/injectme.js: -------------------------------------------------------------------------------- 1 | // Use 'page.injectJs()' to load the script itself in the Page context 2 | 3 | if ( typeof(phantom) !== "undefined" ) { 4 | var page = require('webpage').create(); 5 | 6 | // Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") 7 | page.onConsoleMessage = function(msg) { 8 | console.log(msg); 9 | }; 10 | 11 | page.onAlert = function(msg) { 12 | console.log(msg); 13 | }; 14 | 15 | console.log("* Script running in the Phantom context."); 16 | console.log("* Script will 'inject' itself in a page..."); 17 | page.open("about:blank", function(status) { 18 | if ( status === "success" ) { 19 | console.log(page.injectJs("injectme.js") ? "... done injecting itself!" : "... fail! Check the $PWD?!"); 20 | } 21 | phantom.exit(); 22 | }); 23 | } else { 24 | alert("* Script running in the Page context."); 25 | } 26 | -------------------------------------------------------------------------------- /examples/loadspeed.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(), 2 | system = require('system'), 3 | t, address; 4 | 5 | if (system.args.length === 1) { 6 | console.log('Usage: loadspeed.js '); 7 | phantom.exit(1); 8 | } else { 9 | t = Date.now(); 10 | address = system.args[1]; 11 | page.open(address, function (status) { 12 | if (status !== 'success') { 13 | console.log('FAIL to load the address'); 14 | } else { 15 | t = Date.now() - t; 16 | console.log('Page title is ' + page.evaluate(function () { 17 | return document.title; 18 | })); 19 | console.log('Loading time ' + t + ' msec'); 20 | } 21 | phantom.exit(); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /examples/module.js: -------------------------------------------------------------------------------- 1 | var universe = require('./universe'); 2 | universe.start(); 3 | console.log('The answer is' + universe.answer); 4 | phantom.exit(); 5 | -------------------------------------------------------------------------------- /examples/phantomwebintro.js: -------------------------------------------------------------------------------- 1 | // Read the Phantom webpage '#intro' element text using jQuery and "includeJs" 2 | 3 | var page = require('webpage').create(); 4 | 5 | page.onConsoleMessage = function(msg) { 6 | console.log(msg); 7 | }; 8 | 9 | page.open("http://www.phantomjs.org", function(status) { 10 | if ( status === "success" ) { 11 | page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() { 12 | console.log(page.evaluate(function() { 13 | return $("#intro").text(); 14 | })); 15 | phantom.exit(); 16 | }); 17 | } 18 | }); 19 | 20 | -------------------------------------------------------------------------------- /examples/postserver.js: -------------------------------------------------------------------------------- 1 | // Example using HTTP POST operation 2 | 3 | var page = require('webpage').create(), 4 | server = require('webserver').create(), 5 | system = require('system'), 6 | data = 'universe=expanding&answer=42'; 7 | 8 | if (system.args.length !== 2) { 9 | console.log('Usage: postserver.js '); 10 | phantom.exit(1); 11 | } 12 | 13 | var port = system.args[1]; 14 | 15 | service = server.listen(port, function (request, response) { 16 | console.log('Request received at ' + new Date()); 17 | 18 | response.statusCode = 200; 19 | response.headers = { 20 | 'Cache': 'no-cache', 21 | 'Content-Type': 'text/plain;charset=utf-8' 22 | }; 23 | response.write(JSON.stringify(request, null, 4)); 24 | response.close(); 25 | }); 26 | 27 | page.open('http://localhost:' + port + '/', 'post', data, function (status) { 28 | if (status !== 'success') { 29 | console.log('Unable to post!'); 30 | } else { 31 | console.log(page.plainText); 32 | } 33 | phantom.exit(); 34 | }); 35 | -------------------------------------------------------------------------------- /examples/printenv.js: -------------------------------------------------------------------------------- 1 | var system = require('system'), 2 | env = system.env, 3 | key; 4 | 5 | for (key in env) { 6 | if (env.hasOwnProperty(key)) { 7 | console.log(key + '=' + env[key]); 8 | } 9 | } 10 | phantom.exit(); 11 | -------------------------------------------------------------------------------- /examples/printheaderfooter.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(), 2 | system = require('system'); 3 | 4 | function someCallback(pageNum, numPages) { 5 | return "

someCallback: " + pageNum + " / " + numPages + "

"; 6 | } 7 | 8 | if (system.args.length < 3) { 9 | console.log('Usage: printheaderfooter.js URL filename'); 10 | phantom.exit(1); 11 | } else { 12 | var address = system.args[1]; 13 | var output = system.args[2]; 14 | page.viewportSize = { width: 600, height: 600 }; 15 | page.paperSize = { 16 | format: 'A4', 17 | margin: "1cm", 18 | /* default header/footer for pages that don't have custom overwrites (see below) */ 19 | header: { 20 | height: "1cm", 21 | contents: phantom.callback(function(pageNum, numPages) { 22 | if (pageNum == 1) { 23 | return ""; 24 | } 25 | return "

Header " + pageNum + " / " + numPages + "

"; 26 | }) 27 | }, 28 | footer: { 29 | height: "1cm", 30 | contents: phantom.callback(function(pageNum, numPages) { 31 | if (pageNum == numPages) { 32 | return ""; 33 | } 34 | return "

Footer " + pageNum + " / " + numPages + "

"; 35 | }) 36 | } 37 | }; 38 | page.open(address, function (status) { 39 | if (status !== 'success') { 40 | console.log('Unable to load the address!'); 41 | } else { 42 | /* check whether the loaded page overwrites the header/footer setting, 43 | i.e. whether a PhantomJSPriting object exists. Use that then instead 44 | of our defaults above. 45 | 46 | example: 47 | 48 | 49 | 61 | 62 |

asdfadsf

asdfadsfycvx

63 | 64 | */ 65 | if (page.evaluate(function(){return typeof PhantomJSPrinting == "object";})) { 66 | paperSize = page.paperSize; 67 | paperSize.header.height = page.evaluate(function() { 68 | return PhantomJSPrinting.header.height; 69 | }); 70 | paperSize.header.contents = phantom.callback(function(pageNum, numPages) { 71 | return page.evaluate(function(pageNum, numPages){return PhantomJSPrinting.header.contents(pageNum, numPages);}, pageNum, numPages); 72 | }); 73 | paperSize.footer.height = page.evaluate(function() { 74 | return PhantomJSPrinting.footer.height; 75 | }); 76 | paperSize.footer.contents = phantom.callback(function(pageNum, numPages) { 77 | return page.evaluate(function(pageNum, numPages){return PhantomJSPrinting.footer.contents(pageNum, numPages);}, pageNum, numPages); 78 | }); 79 | page.paperSize = paperSize; 80 | console.log(page.paperSize.header.height); 81 | console.log(page.paperSize.footer.height); 82 | } 83 | window.setTimeout(function () { 84 | page.render(output); 85 | phantom.exit(); 86 | }, 200); 87 | } 88 | }); 89 | } 90 | -------------------------------------------------------------------------------- /examples/printmargins.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(), 2 | system = require('system'); 3 | 4 | console.log(window); 5 | 6 | if (system.args.length < 7) { 7 | console.log('Usage: printmargins.js URL filename LEFT TOP RIGHT BOTTOM'); 8 | console.log(' margin examples: "1cm", "10px", "7mm", "5in"'); 9 | phantom.exit(1); 10 | } else { 11 | var address = system.args[1]; 12 | var output = system.args[2]; 13 | var marginLeft = system.args[3]; 14 | var marginTop = system.args[4]; 15 | var marginRight = system.args[5]; 16 | var marginBottom = system.args[6]; 17 | page.viewportSize = { width: 600, height: 600 }; 18 | page.paperSize = { 19 | format: 'A4', 20 | margin: { 21 | left: marginLeft, 22 | top: marginTop, 23 | right: marginRight, 24 | bottom: marginBottom 25 | } 26 | }; 27 | page.open(address, function (status) { 28 | if (status !== 'success') { 29 | console.log('Unable to load the address!'); 30 | } else { 31 | window.setTimeout(function () { 32 | page.render(output); 33 | phantom.exit(); 34 | }, 200); 35 | } 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | var server = require('webserver').create(); 3 | var system = require('system'); 4 | var host, port; 5 | 6 | if (system.args.length !== 2) { 7 | console.log('Usage: server.js '); 8 | phantom.exit(1); 9 | } else { 10 | port = system.args[1]; 11 | var listening = server.listen(port, function (request, response) { 12 | console.log("GOT HTTP REQUEST"); 13 | console.log(JSON.stringify(request, null, 4)); 14 | 15 | // we set the headers here 16 | response.statusCode = 200; 17 | response.headers = {"Cache": "no-cache", "Content-Type": "text/html"}; 18 | // this is also possible: 19 | response.setHeader("foo", "bar"); 20 | // now we write the body 21 | // note: the headers above will now be sent implictly 22 | response.write("YES!"); 23 | // note: writeBody can be called multiple times 24 | response.write("

pretty cool :)"); 25 | response.close(); 26 | }); 27 | if (!listening) { 28 | console.log("could not create web server listening on port " + port); 29 | phantom.exit(); 30 | } 31 | var url = "http://localhost:" + port + "/foo/bar.php?asdf=true"; 32 | console.log("SENDING REQUEST TO:"); 33 | console.log(url); 34 | page.open(url, function (status) { 35 | if (status !== 'success') { 36 | console.log('FAIL to load the address'); 37 | } else { 38 | console.log("GOT REPLY FROM SERVER:"); 39 | console.log(page.content); 40 | } 41 | phantom.exit(); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /examples/serverkeepalive.js: -------------------------------------------------------------------------------- 1 | var port, server, service, 2 | system = require('system'); 3 | 4 | if (system.args.length !== 2) { 5 | console.log('Usage: serverkeepalive.js '); 6 | phantom.exit(1); 7 | } else { 8 | port = system.args[1]; 9 | server = require('webserver').create(); 10 | 11 | service = server.listen(port, { keepAlive: true }, function (request, response) { 12 | console.log('Request at ' + new Date()); 13 | console.log(JSON.stringify(request, null, 4)); 14 | 15 | var body = JSON.stringify(request, null, 4); 16 | console.log(body.length); 17 | response.statusCode = 200; 18 | response.headers = { 19 | 'Cache': 'no-cache', 20 | 'Content-Type': 'text/plain', 21 | 'Connection': 'Keep-Alive', 22 | 'Keep-Alive': 'timeout=5, max=100', 23 | 'Content-Length': body.length 24 | }; 25 | response.write(body); 26 | response.close(); 27 | }); 28 | 29 | if (service) { 30 | console.log('Web server running on port ' + port); 31 | } else { 32 | console.log('Error: Could not create web server listening on port ' + port); 33 | phantom.exit(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/simpleserver.js: -------------------------------------------------------------------------------- 1 | var port, server, service, 2 | system = require('system'); 3 | 4 | if (system.args.length !== 2) { 5 | console.log('Usage: simpleserver.js '); 6 | phantom.exit(1); 7 | } else { 8 | port = system.args[1]; 9 | server = require('webserver').create(); 10 | 11 | service = server.listen(port, function (request, response) { 12 | 13 | console.log('Request at ' + new Date()); 14 | console.log(JSON.stringify(request, null, 4)); 15 | 16 | response.statusCode = 200; 17 | response.headers = { 18 | 'Cache': 'no-cache', 19 | 'Content-Type': 'text/html' 20 | }; 21 | response.write(''); 22 | response.write(''); 23 | response.write('Hello, world!'); 24 | response.write(''); 25 | response.write(''); 26 | response.write('

This is from PhantomJS web server.

'); 27 | response.write('

Request data:

'); 28 | response.write('
');
29 |         response.write(JSON.stringify(request, null, 4));
30 |         response.write('
'); 31 | response.write(''); 32 | response.write(''); 33 | response.close(); 34 | }); 35 | 36 | if (service) { 37 | console.log('Web server running on port ' + port); 38 | } else { 39 | console.log('Error: Could not create web server listening on port ' + port); 40 | phantom.exit(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/staticserver.js: -------------------------------------------------------------------------------- 1 |  2 | 3 | var server = require('webserver').create(); 4 | var fs = require('fs'); 5 | 6 | server.listen(8897, function(request, response) { 7 | console.log('requested url--> ' + request.url); 8 | var path = request.url; 9 | if (!path || path === '/') path = '/index.html'; 10 | response.write(fs.read('www' + path)); 11 | response.close(); 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /examples/test.js: -------------------------------------------------------------------------------- 1 |  2 | /* 3 | 4 | 5 | 6 | var page = new WebPage(); 7 | 8 | var msg = "message", 9 | value = "value", 10 | result, 11 | expected = "extra-value"; 12 | page.onPrompt = function(msg, value) { 13 | return "extra-"+value; 14 | }; 15 | result = page.evaluate(function(m, v) { 16 | return window.prompt(m, v); 17 | }, msg, value); 18 | console.log("checkPagePrompt:result(should be : " + expected + "):" + result); 19 | 20 | 21 | 22 | var sys = require("system"); 23 | 24 | console.log("sys.pid: " + sys.pid); 25 | console.log("sys.os: ", sys.os); 26 | console.log("sys.env: ", sys.env); 27 | 28 | */ 29 | 30 | /* 31 | var page = new WebPage(); 32 | page.open("http://www.google.com", function() { 33 | console.log("page.url", page.url); 34 | console.log("page.title", page.title); 35 | console.log("page.plainText", page.plainText); 36 | }) 37 | */ 38 | 39 | var fs = require("fs"); 40 | var path = "examples/test.js"; 41 | 42 | var pathInfo = { 43 | absolute: fs.absolute(path), 44 | list: fs.list(path), 45 | size: Math.round(fs.size(path) / 1024), 46 | exists: fs.exists(path), 47 | isDirectory: fs.isDirectory(path), 48 | isFile: fs.isFile(path) 49 | } 50 | 51 | console.log("pathInfo:", pathInfo); 52 | 53 | if (pathInfo.isFile) { 54 | console.log("Reading file " + path); 55 | console.log(fs.read(path)); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /examples/universe.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | // This is to be used by "module.js" (and "module.coffee") example(s). 4 | // There should NOT be a "universe.coffee" as only 1 of the 2 would 5 | // ever be loaded unless the file extension was specified. 6 | 7 | exports.answer = 42; 8 | 9 | exports.start = function () { 10 | console.log('Starting the universe....'); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /examples/variable.js: -------------------------------------------------------------------------------- 1 |  2 | // This is a simple example of how to set a variable 3 | // Its used internally for testing script injection 4 | // phantom.injectJs(); 5 | 6 | var ___test190234 = [] instanceof Array; 7 | -------------------------------------------------------------------------------- /examples/version.js: -------------------------------------------------------------------------------- 1 |  2 | 3 | var phantom_version = Object.keys(phantom.version).map(function(k) {return phantom.version[k]}); 4 | var trifle_version = Object.keys(trifle.version).map(function(k) {return trifle.version[k]}); 5 | var ie_version = trifle.emulation; 6 | 7 | console.log('phantomjs -> ' + phantom_version.join('.')); 8 | console.log('triflejs -> ' + trifle_version.join('.')); 9 | console.log('internet explorer -> ' + ie_version); 10 | console.log('userAgent -> ' + navigator.userAgent); 11 | 12 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/favicon.ico -------------------------------------------------------------------------------- /includes/trifle/Callback.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Callback.js 3 | * 4 | * By: Steven de Salas 5 | * On: Sep 2013 6 | * 7 | * Generates a Callback object used for async 8 | * communication between V8 and C# runtime. 9 | * 10 | */ 11 | 12 | // Initialise Namespace 13 | this.trifle = this.trifle || {}; 14 | 15 | // Wrap code to avoid global vars 16 | (function(trifle) { 17 | 18 | // Closure variable that tracks existing callbacks 19 | // (hidden from outside world) 20 | var callbacks = {}; 21 | 22 | // Callback Class 23 | // Define Constructor 24 | var Callback = trifle.Callback = function(func, scope, defaultArgs) { 25 | this.func = func; 26 | this.scope = scope; 27 | this.defaultArgs = defaultArgs; 28 | this.id = Callback.newUid(); 29 | console.xdebug('new Callback#' + this.id + '(func, scope, defaultArgs)'); 30 | callbacks[this.id] = this; 31 | }; 32 | 33 | // Unique ID Generator 34 | Callback.newUid = function() { 35 | var s4 = function() { 36 | return Math.floor((1 + Math.random()) * 0x10000) 37 | .toString(16) 38 | .substring(1); 39 | }; 40 | return s4() + s4(); 41 | }; 42 | 43 | // Execute callback 44 | Callback.execute = function(id, args) { 45 | console.xdebug('Callback.execute("' + id + '", [args])'); 46 | var callback = callbacks[id]; 47 | if (callback) { 48 | if (!args || !args.length) { 49 | args = callback.defaultArgs; 50 | } 51 | callback.func.apply(callback.scope || callback, args); 52 | } 53 | } 54 | 55 | // Execute callback and delete reference 56 | Callback.executeOnce = function(id, args) { 57 | console.xdebug('Callback.executeOnce("' + id + '", [args])'); 58 | if (typeof id === 'string') { 59 | Callback.execute(id, args); 60 | delete callbacks[id]; 61 | } 62 | } 63 | 64 | // Generates a callback and returns the id 65 | Callback.id = function(func, scope, defaultArgs) { 66 | return (new Callback(func, scope, defaultArgs)).id; 67 | }; 68 | 69 | 70 | })(this.trifle); 71 | 72 | -------------------------------------------------------------------------------- /includes/trifle/ie/tools.js: -------------------------------------------------------------------------------- 1 |  2 | /** 3 | * TRIFLEJS IE TOOLS 4 | * By: Steven de Salas 5 | */ 6 | 7 | // Add OnCallback functionality 8 | window.callPhantom = function() { 9 | window.external.xdebug('window.callPhantom(args)'); 10 | var args = []; 11 | for (var i = 0; i < arguments.length; i++) { 12 | args.push(arguments[i]); 13 | } 14 | return window.external.fireEvent('callback', JSON.stringify(args)); 15 | } 16 | 17 | // Override javascript alert 18 | window.alert = function(message) { 19 | window.external.xdebug('window.alert()'); 20 | message = message + ""; 21 | return window.external.fireEvent('alert', JSON.stringify([message])); 22 | } 23 | 24 | // Override javascript confirm 25 | window.confirm = function(message) { 26 | window.external.xdebug('window.confirm()'); 27 | message = message + ""; 28 | return window.external.fireEvent('confirm', JSON.stringify([message])); 29 | } 30 | 31 | // Override javascript prompt. 32 | window.prompt = function(message, defaultValue) { 33 | window.external.xdebug('window.prompt()'); 34 | message = message + ""; 35 | return window.external.fireEvent('prompt', JSON.stringify([message, defaultValue || ""])); 36 | } 37 | 38 | // Capture javascript errors 39 | window.onerror = function(msg, url, line, col, e) { 40 | var caller = arguments.callee ? arguments.callee.caller : ''; 41 | var trace = [{file: url, line: line, col: col, 'function': caller.toString()}]; 42 | while (!!caller.caller) { 43 | trace.push({func: caller.caller.toString()}); 44 | caller = caller.caller; 45 | } 46 | return window.external.fireEvent('error', JSON.stringify([msg, trace])); 47 | } 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /includes/trifle/modules/ChildProcess.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ChildProcess.js 3 | * 4 | * By: Steven de Salas 5 | * On: Jan 2015 6 | * 7 | * Defines a ChildProcess class representing a 8 | * helper to spawn and manage new child processes 9 | * 10 | */ 11 | 12 | // Initialise Namespace 13 | this.trifle = this.trifle || {}; 14 | trifle.modules = trifle.modules || {}; 15 | 16 | // Wrap code to avoid global vars 17 | (function(trifle) { 18 | 19 | // Private 20 | var Callback = trifle.Callback; 21 | 22 | // Define Module 23 | var ChildProcess = trifle.modules.ChildProcess = trifle.extend({ 24 | 25 | // Derives functionality from ChildProcess.cs 26 | module: trifle.API.ChildProcess, 27 | 28 | // Constructor 29 | init: function() { 30 | console.xdebug("new ChildProcess()"); 31 | }, 32 | 33 | // Additional methods 34 | methods: { 35 | // Decorate a childprocess context with event listeners 36 | // to support 'stdout' and 'stderr' events 37 | // as well as their counterpart 'fakestream' objects 38 | decorate: function(ctx) { 39 | if (ctx) { 40 | // Add StdOut and StdErr Events 41 | Object.addEvent(ctx, "onExit"); 42 | Object.addEvent(ctx, "onStdOut"); 43 | Object.addEvent(ctx, "onStdErr"); 44 | // Pipe output to stdout and stderr 'fakestream' objects 45 | ctx.stdout = {}; 46 | ctx.stderr = {}; 47 | Object.addEvent(ctx.stdout, "onData"); 48 | Object.addEvent(ctx.stderr, "onData"); 49 | ctx.on('stdout', function(data) { 50 | ctx.stdout.fireEvent('data', [data]); 51 | }); 52 | ctx.on('stderr', function(data) { 53 | ctx.stderr.fireEvent('data', [data]); 54 | }); 55 | } 56 | return ctx; 57 | }, 58 | spawn: function(cmd, args, opts) { 59 | // Add event listeners, spawn and return context object 60 | var context; 61 | var exit = function(id, code) {context.fireEvent('exit', [code]);}; 62 | var stdout = function(data) {context.fireEvent('stdout', [data]);}; 63 | var stderr = function(data) {context.fireEvent('stderr', [data]);}; 64 | context = this._spawn(cmd, args || [], opts, Callback.id(exit), Callback.id(stdout), Callback.id(stderr)); 65 | return this.decorate(context); 66 | }, 67 | execFile: function(cmd, args, opts, callback) { 68 | var cp = this; 69 | var complete = function(contextId) { 70 | // Execute callback 71 | var context = cp._findContext(contextId); 72 | if (context && callback && callback.call) { 73 | return callback.call(cp, null, context.output, context.errorOutput); 74 | } 75 | }; 76 | // Execute and return context object 77 | return this._execFile(cmd, args || [], opts, Callback.id(complete)); 78 | }, 79 | execSync: function(cmd, args, opts) { 80 | if (cmd) { 81 | return this._execSync(cmd, args || [], opts); 82 | } 83 | } 84 | } 85 | 86 | }); 87 | 88 | 89 | })(this.trifle); 90 | 91 | -------------------------------------------------------------------------------- /includes/trifle/modules/FileSystem.js: -------------------------------------------------------------------------------- 1 | /* 2 | * FileSystem.js 3 | * 4 | * By: Steven de Salas 5 | * On: Sep 2013 6 | * 7 | * Defines a FileSystem class representing a 8 | * helper for file read/write operations and management. 9 | * 10 | */ 11 | 12 | // Initialise Namespace 13 | this.trifle = this.trifle || {}; 14 | trifle.modules = trifle.modules || {}; 15 | 16 | // Wrap code to avoid global vars 17 | (function(trifle) { 18 | 19 | 20 | // Define Module 21 | var FileSystem = trifle.modules.FileSystem = trifle.extend({ 22 | 23 | // Derives functionality from FileSystem.cs 24 | module: trifle.API.FileSystem, 25 | 26 | // Constructor 27 | init: function() { 28 | console.xdebug("new FileSystem()"); 29 | }, 30 | 31 | // Additional methods 32 | methods: { 33 | 34 | } 35 | 36 | }); 37 | 38 | 39 | })(this.trifle); 40 | 41 | -------------------------------------------------------------------------------- /includes/trifle/modules/System.js: -------------------------------------------------------------------------------- 1 | /* 2 | * System.js 3 | * 4 | * By: Steven de Salas 5 | * On: Sep 2013 6 | * 7 | * Defines a System class representing a 8 | * general program helper. 9 | * 10 | */ 11 | 12 | // Initialise Namespace 13 | this.trifle = this.trifle || {}; 14 | trifle.modules = trifle.modules || {}; 15 | 16 | // Wrap code to avoid global vars 17 | (function(trifle) { 18 | 19 | 20 | // Define Module 21 | var System = trifle.modules.System = trifle.extend({ 22 | 23 | // Derives functionality from System.cs 24 | module: trifle.API.System, 25 | 26 | // Constructor 27 | init: function() { 28 | console.xdebug("new System()"); 29 | } 30 | 31 | }); 32 | 33 | 34 | })(this.trifle); 35 | 36 | -------------------------------------------------------------------------------- /includes/trifle/modules/WebPage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * WebPage.js 3 | * 4 | * By: Steven de Salas 5 | * On: Sep 2013 6 | * 7 | * Defines an object representing a 8 | * browser page opened inside IE environment. 9 | * 10 | */ 11 | 12 | // Initialise Namespace 13 | this.trifle = this.trifle || {}; 14 | this.trifle.modules = this.trifle.modules || {}; 15 | 16 | // Wrap code to avoid global vars 17 | (function (trifle) { 18 | 19 | // Private 20 | var Callback = trifle.Callback; 21 | 22 | // Define Module 23 | var WebPage = this.WebPage = window.WebPage = trifle.modules.WebPage = trifle.extend({ 24 | 25 | // Derives functionality from WebPage.cs 26 | module: trifle.API.WebPage, 27 | 28 | // Constructor 29 | init: function() { 30 | console.xdebug("new WebPage()"); 31 | // Properties 32 | this.objectName = "WebPage"; 33 | // Store reference 34 | WebPage.all[this.uuid] = this; 35 | // Add Events 36 | Object.addEvent(this, 'onCallback'); 37 | Object.addEvent(this, 'onInitialized'); 38 | Object.addEvent(this, 'onAlert'); 39 | Object.addEvent(this, 'onConfirm', true); // unique (uses return value) 40 | Object.addEvent(this, 'onPrompt', true); // unique (uses return value) 41 | Object.addEvent(this, 'onError'); 42 | Object.addEvent(this, 'onLoadStarted'); 43 | Object.addEvent(this, 'onLoadFinished'); 44 | Object.addEvent(this, 'onUrlChanged'); 45 | Object.addEvent(this, 'onNavigationRequested'); 46 | // Add event route between 'internalpagecreated' and 'pagecreated' 47 | // this allows us to use objects from V8 context 48 | // (regardless of where trigger comes from) when firing 'pagecreated'. 49 | Object.addEvent(this, 'onInternalPageCreated'); 50 | Object.addEvent(this, 'onPageCreated'); 51 | this.on('internalpagecreated', function() { 52 | return this.fireEvent('pagecreated'); 53 | }); 54 | // Add event route between 'internalclosing' and 'closing' 55 | // using same logic as above. 56 | Object.addEvent(this, 'onInternalClosing'); 57 | Object.addEvent(this, 'onClosing'); 58 | this.on('internalclosing', function() { 59 | return this.fireEvent('closing', [this]); 60 | }); 61 | // Clear the COM event pipeline 62 | trifle.wait(1); 63 | }, 64 | 65 | // Additional methods 66 | methods: { 67 | 68 | // Opens a URL 69 | open: function() { 70 | console.xdebug("WebPage.prototype.open()"); 71 | var page = this, a = arguments; 72 | // Determine the arguments to use 73 | var url = a[0], method = "GET", data, headers, callback; 74 | // Using: page.open(url, method, data, callback) 75 | if (typeof a[4] === "function") { 76 | method = a[1]; 77 | data = a[2]; 78 | headers = a[3]; 79 | callback = a[4]; 80 | } 81 | // Using: page.open(url, method, data, callback) 82 | if (typeof a[3] === "function") { 83 | method = a[1]; 84 | data = a[2]; 85 | callback = a[3]; 86 | } 87 | // Using: page.open(url, method, callback) 88 | else if (typeof a[2] === "function") { 89 | method = a[1]; 90 | callback = a[2]; 91 | } 92 | // Using: page.open(url, callback) 93 | else if (typeof a[1] === "function") { 94 | callback = a[1]; 95 | } 96 | // Instantiate Callback 97 | var complete = function(status) { 98 | // Execute callback 99 | if (callback && callback.call) { 100 | return !!callback ? callback.call(page, status) : null; 101 | } 102 | }; 103 | // Open URL in .NET API 104 | return this._open(url, method, data, Callback.id(complete)); 105 | }, 106 | 107 | // Executes a JavaScript code string in the browser 108 | evaluateJavaScript: function(code) { 109 | console.xdebug("WebPage.prototype.evaluateJavaScript(code)"); 110 | if (code && typeof code === "string") { 111 | // Execute JS on IE host 112 | return this._evaluateJavaScript(code); 113 | } 114 | }, 115 | 116 | 117 | // Executes a javascript function inside the browser 118 | evaluate: function(func) { 119 | console.xdebug("WebPage.prototype.evaluate(func)"); 120 | if (!(typeof func === 'function' || typeof func === 'string' || func instanceof String)) { 121 | throw "Wrong use of WebPage.evaluate()"; 122 | } 123 | var args = []; 124 | for (var i = 1; i < arguments.length; i++) { 125 | // Fix undefined (coming up as null) 126 | if (arguments[i] === undefined) { 127 | arguments[i] = "{{undefined}}"; 128 | } 129 | args.push(arguments[i]); 130 | } 131 | // Execute JS on IE host 132 | return JSON.parse(this._evaluate(func.toString(), args)); 133 | }, 134 | 135 | // Evaluations a function in the context of the page without 136 | // blocking execution. Can be delayed by a specific timeout. 137 | evaluateAsync: function(func, timeMs) { 138 | console.xdebug("WebPage.prototype.evaluateAsync(func)"); 139 | if (!(typeof func === 'function' || typeof func === 'string' || func instanceof String)) { 140 | throw "Wrong use of WebPage.evaluateAsync()"; 141 | } 142 | var page = this, args = Array.prototype.slice.call(arguments, 2); 143 | args.unshift(func); 144 | window.setTimeout(function() { 145 | page.evaluate.apply(page, args); 146 | }, timeMs || 0); 147 | }, 148 | 149 | // Injects a local JavaScript file into the browser 150 | injectJs: function(filename) { 151 | console.xdebug("WebPage.prototype.injectJs(filename)"); 152 | if (typeof filename === 'string') { 153 | // Execute JS on IE host 154 | return this._injectJs(filename); 155 | } 156 | }, 157 | 158 | // Includes a JS file from remote URL and executes it on the browser 159 | includeJs: function(url, callback) { 160 | console.xdebug("WebPage.prototype.includeJs(url, callback)"); 161 | var page = this; 162 | if (typeof url === 'string') { 163 | var complete = function() { 164 | if (callback && callback.call) { 165 | callback.call(page); 166 | } 167 | }; 168 | // Execute JS on IE host 169 | return this._includeJs(url, Callback.id(complete)); 170 | } 171 | }, 172 | 173 | // Sends mouse/keyboard event to the browser 174 | sendEvent: function(type) { 175 | console.xdebug("WebPage.prototype.sendEvent(type)"); 176 | return this._sendEvent(type, Array.prototype.slice.call(arguments, 1)); 177 | }, 178 | 179 | /** 180 | * Renders screen to a file 181 | * @param filename 182 | * @return {*} 183 | */ 184 | render: function(filename) { 185 | console.xdebug("WebPage.prototype.render(filename)"); 186 | if (filename) { 187 | return this._render(filename) 188 | }; 189 | }, 190 | 191 | /** 192 | * Renders screen to base64 string 193 | * @param format 194 | * @return {*} 195 | */ 196 | renderBase64: function(format) { 197 | console.xdebug("WebPage.prototype.renderBase64(format)"); 198 | return this._renderBase64(format || "PNG"); 199 | }, 200 | 201 | /** 202 | * Closes current window and disposes of resources 203 | */ 204 | close: function() { 205 | this.fireEvent('internalclosing'); 206 | this._close(); 207 | } 208 | 209 | } 210 | 211 | }); 212 | 213 | // STATIC PROPERTIES 214 | 215 | // HashMap of instantiated pages 216 | WebPage.all = {}; 217 | 218 | // Add static fireEvent() method for event handling 219 | WebPage.fireEvent = function(nickname, uuid, args) { 220 | console.xdebug("WebPage.fireEvent('" + nickname + "', uuid, args)"); 221 | var page = WebPage.all[uuid]; 222 | if (page && page.fireEvent) { 223 | return page.fireEvent(nickname, args); 224 | } 225 | }; 226 | 227 | 228 | })(this.trifle); 229 | 230 | -------------------------------------------------------------------------------- /includes/trifle/modules/WebServer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * WebServer.js 3 | * 4 | * By: Steven de Salas 5 | * On: Jan 2014 6 | * 7 | * Defines a WebServer class representing a 8 | * HTTP Daemon. 9 | * 10 | */ 11 | 12 | // Initialise Namespace 13 | this.trifle = this.trifle || {}; 14 | trifle.modules = trifle.modules || {}; 15 | 16 | // Wrap code to avoid global vars 17 | (function (trifle) { 18 | 19 | // Private 20 | var Callback = trifle.Callback; 21 | 22 | // Define Module 23 | var WebServer = trifle.modules.WebServer = trifle.extend({ 24 | 25 | // Derives functionality from WebServer.cs 26 | module: trifle.API.WebServer, 27 | 28 | // Constructor 29 | init: function() { 30 | console.xdebug("new WebServer()"); 31 | // Properties 32 | this.objectName = "WebServer"; 33 | }, 34 | 35 | // Additional methods 36 | methods: { 37 | 38 | // Listen for incoming requests 39 | listen: function(binding, opts, callback) { 40 | console.xdebug("Webserver.prototype.listen(binding, opts, callback)"); 41 | var API = this; 42 | if (typeof callback === 'undefined' && typeof opts === 'function') { 43 | callback = opts; 44 | opts = {}; 45 | } 46 | 47 | // Instantiate Callback 48 | var complete = function(connectionId) { 49 | // Get Request & Response 50 | var request = API._getRequest(connectionId); 51 | var response = API._getResponse(connectionId); 52 | // Decorate request object so it can be printed using console.log() or JSON.stringify() 53 | request.headers = request._headers; 54 | request.httpVersion = request._httpVersion; 55 | request.method = request._method; 56 | if (request.method === "POST") 57 | { 58 | request.post = request._post; 59 | request.postRaw = request._postRaw; 60 | } 61 | request.url = request._url; 62 | // Execute callback 63 | if (callback && callback.call) { 64 | var result = !!callback ? callback.call(API, request, response) : null; 65 | if (!opts.keepAlive) { 66 | response.close(); 67 | } 68 | return result; 69 | } 70 | }; 71 | // Start listening on binding 72 | return API._listen(binding, Callback.id(complete)); 73 | } 74 | } 75 | }); 76 | 77 | 78 | })(this.trifle); 79 | 80 | -------------------------------------------------------------------------------- /test/feature/console.log.js: -------------------------------------------------------------------------------- 1 |  2 | 3 | /* console.log() */ 4 | 5 | console.log(1); 6 | console.log('a string'); 7 | console.log(null); 8 | console.log(NaN); 9 | console.log(undefined); 10 | console.log(function() { var a = 1; }); 11 | console.log({an: 'object'}); 12 | console.log([1, 'array']); 13 | 14 | phantom.exit(); -------------------------------------------------------------------------------- /test/feature/fs.changeWorkingDirectory.js: -------------------------------------------------------------------------------- 1 |  2 | var fs = require('fs'); 3 | 4 | console.log("Current Working Dir: " + fs.workingDirectory); 5 | fs.changeWorkingDirectory("C:/"); 6 | console.log("Current Working Dir: " + fs.workingDirectory); 7 | 8 | phantom.exit(); 9 | 10 | -------------------------------------------------------------------------------- /test/feature/opts.ignore-ssl.js: -------------------------------------------------------------------------------- 1 |  2 | // triflejs.exe ..\..\test\feature\opts.ignore-ssl.js --ignore-ssl-errors=true 3 | 4 | var page = require("webpage").create(); 5 | 6 | page.open("https://localhost", function(status) { 7 | if (status === 'success') { 8 | page.render("ingnore-ssl.png"); 9 | console.log('Page rendered'); 10 | } else { 11 | console.error('Cannot load url.'); 12 | } 13 | phantom.exit(); 14 | }); 15 | -------------------------------------------------------------------------------- /test/feature/opts.proxy.js: -------------------------------------------------------------------------------- 1 |  2 | // triflejs.exe ..\..\test\feature\opts.proxy.js --proxy=localhost:8080 --proxy-auth=MacUser:123456 3 | 4 | var page = require("webpage").create(); 5 | 6 | page.open("http://www.triflejs.org", function(status) { 7 | if (status === 'success') { 8 | page.render("triflejs.org.png"); 9 | console.log('Page rendered'); 10 | } else { 11 | console.error('Cannot load url.'); 12 | } 13 | phantom.exit(); 14 | }); 15 | 16 | -------------------------------------------------------------------------------- /test/feature/page.clipRect.js: -------------------------------------------------------------------------------- 1 | var page = require("webpage").create(); 2 | /* 3 | page.clipRect = { 4 | left: 150, 5 | top: 150, 6 | width: 200, 7 | height: 200 8 | }; 9 | */ 10 | // page.zoomFactor = 2; 11 | 12 | page.open("http://www.triflejs.org", function(status) { 13 | if (status === 'success') { 14 | page.render("triflejs.org.png"); 15 | console.log('Page rendered'); 16 | } else { 17 | console.error('Cannot load url.'); 18 | } 19 | phantom.exit(); 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /test/feature/page.customHeaders.js: -------------------------------------------------------------------------------- 1 |  2 | 3 | var page = require("webpage").create(); 4 | 5 | page.customHeaders = { 6 | 'X-Test': 'foo', 7 | 'DNT': 1, 8 | 'scooby': 'doo' 9 | } 10 | 11 | page.open("http://www.xhaus.com/headers", function(status) { 12 | if (status === 'success') { 13 | page.render("headers.png"); 14 | console.log('Page rendered'); 15 | } else { 16 | console.error('Cannot load url.'); 17 | } 18 | phantom.exit(); 19 | }); -------------------------------------------------------------------------------- /test/feature/page.evaluate.js: -------------------------------------------------------------------------------- 1 | var page = require("webpage").create(); 2 | 3 | console.log(trifle); 4 | 5 | page.open("http://www.phantomjs.org", function(status) { 6 | if (status === 'success') { 7 | var message = page.evaluate(function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) { 8 | var result = arg1 + arg2 + arg3[0] + arg3[1] + arg4.a + ' ' + arg5 + arg6; 9 | if (arg7 === null) { 10 | result += arg7; 11 | }; 12 | if (arg8 === undefined) { 13 | result += arg8; 14 | } 15 | return result; 16 | }, 'message 1', 234, [5, '6'], { a: "78" }, !true, NaN, null, undefined); 17 | if (message === 'message 12345678 falseNaNnullundefined') { 18 | console.log('GOOD! ' + message); 19 | } else { 20 | console.error('ERROR! ' + message); 21 | } 22 | } else { 23 | console.error('ERROR! Cannot load url.'); 24 | } 25 | phantom.exit(); 26 | }); -------------------------------------------------------------------------------- /test/feature/page.evaluateJavaScript.js: -------------------------------------------------------------------------------- 1 | var page = require("webpage").create(); 2 | 3 | page.open("http://www.phantomjs.org", function(status) { 4 | if (status === 'success') { 5 | page.evaluateJavaScript('var message = "hello from ie";'); 6 | var message = page.evaluate(function() { 7 | return message; 8 | }); 9 | if (message === 'hello from ie') { 10 | console.log('GOOD!' + message); 11 | } else { 12 | console.error('ERROR! ' + message); 13 | } 14 | } else { 15 | console.error('ERROR! Cannot load url.'); 16 | } 17 | phantom.exit(); 18 | }); -------------------------------------------------------------------------------- /test/feature/page.eventLifecycle.js: -------------------------------------------------------------------------------- 1 |  2 | var page = require('webpage').create(); 3 | var server = require('webserver').create(); 4 | 5 | // Start a listener to check events 6 | server.listen(8083, function(request, response) { 7 | var bodyText = JSON.stringify({ 8 | success: true, 9 | url: request.url 10 | }); 11 | response.write('eventtest9837423401' + 12 | '' + 13 | '' + bodyText + ''); 14 | response.close(); 15 | }); 16 | 17 | 18 | page.onLoadStarted = function() { 19 | console.log("LoadStarted"); 20 | } 21 | 22 | page.onLoadFinished = function(status) { 23 | console.log("LoadFinished"); 24 | } 25 | page.onInitialized = function () { 26 | console.log("Initialized"); 27 | } 28 | page.onCallback = function(event) { 29 | console.log('Callback: ' + event); 30 | }; 31 | page.evaluate(function () { 32 | callPhantom('beforeopen'); 33 | }); 34 | page.open('http://localhost:8083/', function (status) { 35 | //phantom.exit(); 36 | }); 37 | 38 | 39 | -------------------------------------------------------------------------------- /test/feature/page.includeJs.js: -------------------------------------------------------------------------------- 1 | var page = require("webpage").create(); 2 | 3 | page.open("http://www.phantomjs.org", function(status) { 4 | page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() { 5 | console.log(page.evaluate(function() { 6 | return $("#intro").text(); 7 | })); 8 | console.wait(5000); 9 | phantom.exit(); 10 | }); 11 | }); -------------------------------------------------------------------------------- /test/feature/page.injectJs.js: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /test/feature/page.js: -------------------------------------------------------------------------------- 1 | var page = require("webpage").create(); 2 | 3 | page.open("http://www.phantomjs.org", function(status) { 4 | console.log("Page load finished, status: " + status); 5 | page.evaluateJavaScript('var message = "hello from ie";'); 6 | console.log(page.evaluate(function(message1, message2, message3) { return message + message1 + message2 + message3; }, 'hello argument1', 'argument2', 3)); 7 | page.render("phantomjs.org.png"); 8 | page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() { 9 | console.log(page.evaluate(function() { 10 | return $("#intro").text(); 11 | })); 12 | trifle.wait(5000); 13 | phantom.exit(); 14 | }); 15 | }); -------------------------------------------------------------------------------- /test/feature/page.onAlert.js: -------------------------------------------------------------------------------- 1 | var page = new WebPage(); 2 | page.onAlert = function(data, data2) { 3 | console.log(data, data2); 4 | } 5 | 6 | page.evaluateJavaScript("alert('onAlert Test');"); 7 | 8 | -------------------------------------------------------------------------------- /test/feature/page.onCallback.js: -------------------------------------------------------------------------------- 1 | console.log("STARTING"); 2 | var page = new WebPage(); 3 | page.onCallback = function(data, data2) { 4 | console.log(data, data2); 5 | } 6 | 7 | page.evaluateJavaScript("callPhantom({a: 1}, 'blah');"); 8 | console.log("DONE!"); 9 | 10 | console.log("STARTING ROUND 2"); 11 | var page = new WebPage(); 12 | var msgA = "a", 13 | msgB = "b", 14 | result, 15 | expected = msgA + msgB; 16 | 17 | page.onCallback = function(a, b) { 18 | console.log("page.onCallback()", a, b); 19 | return a + b; 20 | }; 21 | 22 | result = page.evaluate(function(a, b) { 23 | var x = callPhantom(a, b); 24 | alert("callPhantom() complete: " + x); 25 | return x; 26 | }, msgA, msgB); 27 | 28 | console.log("RESULT:" + result); 29 | console.log("DONE!"); 30 | phantom.exit(); -------------------------------------------------------------------------------- /test/feature/page.onConfirm.js: -------------------------------------------------------------------------------- 1 | var page = new WebPage(); 2 | 3 | page.onAlert = function(message) { 4 | console.log("onAlert: " + message); 5 | } 6 | 7 | page.onConfirm = function(message) { 8 | console.log("onConfirm", message); 9 | return false; 10 | } 11 | 12 | page.evaluateJavaScript("alert(confirm('onAlert Test'));"); 13 | 14 | -------------------------------------------------------------------------------- /test/feature/page.open.js: -------------------------------------------------------------------------------- 1 | var page = require("webpage").create(); 2 | var server = require("webserver").create(); 3 | 4 | server.listen(8080, function(request, response) { 5 | console.log({ 6 | method: request.method, 7 | url: request.url, 8 | httpVersion: request.httpVersion, 9 | headers: request.headers, 10 | post: request.post, 11 | rawPost: request.rawPost 12 | }); 13 | response.close(); 14 | }); 15 | 16 | // GET 17 | page.open("http://localhost:8080/get", function(status) { 18 | if (status !== 'success') { 19 | console.error('Cannot load url.'); 20 | } 21 | }); 22 | 23 | 24 | page.customHeaders = { 25 | 'X-Test': 'foo', 26 | 'DNT': 1, 27 | 'scooby': 'doo' 28 | } 29 | 30 | //POST 31 | page.open("http://localhost:8080/post?custom-headers", "POST", "blah", function(status) { 32 | if (status !== 'success') { 33 | console.error('Cannot load url.'); 34 | } 35 | }); 36 | 37 | 38 | // FORM POST 39 | page.open("http://localhost:8080/post?form-data", 'post', "universe=expanding&answer=42", { "Content-Type" : "application/x-www-form-urlencoded" }, function (status) { 40 | if (status !== 'success') { 41 | console.error('Cannot load url.'); 42 | } 43 | phantom.exit(); 44 | }); 45 | -------------------------------------------------------------------------------- /test/feature/page.open.x2.js: -------------------------------------------------------------------------------- 1 |  2 | var page = new WebPage(); 3 | 4 | // Open first page 5 | page.viewportSize = {width: 1024, height: 800}; 6 | page.open("http://en.wikipedia.org/", function(status) { 7 | if (status !== "success") { console.log("FAIL to load the address"); phantom.exit(); } 8 | page.render('open1.png'); 9 | 10 | page.open("http://google.com/"); 11 | 12 | // Open second page 13 | page.open("http://en.wikipedia.org/wiki/V8_(JavaScript_engine)", function(status) { 14 | if (status !== "success") { console.log("FAIL to load the address"); phantom.exit(); } 15 | page.render('open2.png'); 16 | console.log("Success, both pages opened."); 17 | phantom.exit(); // last open so exit 18 | }); 19 | 20 | }); -------------------------------------------------------------------------------- /test/feature/page.render.js: -------------------------------------------------------------------------------- 1 | var page = require("webpage").create(); 2 | 3 | page.open("http://www.triflejs.org", function(status) { 4 | if (status === 'success') { 5 | page.render("triflejs.org.png"); 6 | console.log('Page rendered'); 7 | } else { 8 | console.error('Cannot load url.'); 9 | } 10 | phantom.exit(); 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /test/feature/page.render.noscrollbar.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | var url = 'http://wikipedia.org'; 3 | var width = 1366; 4 | var height = 1768; 5 | var file = 'test.noscrollbar.png'; 6 | 7 | page.viewportSize = { width: width, height: height }; 8 | 9 | page.open(url, function() { 10 | page.render(file); 11 | phantom.exit(); 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /test/feature/page.viewportSize.js: -------------------------------------------------------------------------------- 1 |  2 | var page = new WebPage(); 3 | page.viewportSize = {width: 800, height: 640}; 4 | page.open("http://www.triflejs.org", function() { 5 | page.render("triflejs.800x640.png"); 6 | }); -------------------------------------------------------------------------------- /test/feature/page.zoomFactor.js: -------------------------------------------------------------------------------- 1 | var page = require("webpage").create(); 2 | 3 | page.zoomFactor = 0.5; 4 | page.open("http://www.triflejs.org", function(status) { 5 | if (status === 'success') { 6 | page.render("triflejs.org.png"); 7 | console.log('Page rendered'); 8 | } else { 9 | console.error('Cannot load url.'); 10 | } 11 | phantom.exit(); 12 | }); 13 | 14 | -------------------------------------------------------------------------------- /test/feature/phantom.injectJs.js: -------------------------------------------------------------------------------- 1 |  2 | console.log(phantom); 3 | 4 | var success = phantom.injectJs('..\\..\\test\\lib\\jasmine.js'); 5 | 6 | if (!success) { 7 | console.error('File not found'); 8 | phantom.exit(); 9 | } 10 | 11 | if (jasmine) { 12 | console.log('Jasmine library injected ok'); 13 | } else { 14 | console.error('Could not inject Jasmine library'); 15 | } 16 | phantom.exit(); 17 | -------------------------------------------------------------------------------- /test/feature/phantom.libraryPath.js: -------------------------------------------------------------------------------- 1 |  2 | 3 | var path = phantom.libraryPath; 4 | 5 | if (path) { 6 | console.log('Library Path: ' + path); 7 | } else { 8 | console.error('Library Path not set!'); 9 | } 10 | -------------------------------------------------------------------------------- /test/feature/system.js: -------------------------------------------------------------------------------- 1 | var sys = new trifle.modules.System(); 2 | var sys2 = new trifle.modules.System(); 3 | 4 | console.log('created'); 5 | 6 | 7 | sys.blah('I am sys'); 8 | sys2.blah('I am sys2'); 9 | 10 | console.log({ 11 | sys: sys, 12 | pid: sys.pid, 13 | args: sys.args, 14 | prototype: trifle.modules.System.prototype 15 | }); 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/feature/webserver.listen.js: -------------------------------------------------------------------------------- 1 | var server = require('webserver').create(); 2 | console.log('Starting webserver on port 8080'); 3 | 4 | var service = server.listen(8080, function(request, response) { 5 | console.log('starting connection, printing request info'); 6 | console.log({ 7 | method: request.method, 8 | url: request.url, 9 | httpVersion: request.httpVersion, 10 | headers: request.headers, 11 | post: request.post, 12 | rawPost: request.rawPost 13 | }); 14 | 15 | response.statusCode = 200; 16 | console.log('adding response.headers', response.headers); 17 | response.headers = { 18 | "header1": "header1", 19 | "header2": "header2" 20 | }; 21 | console.log('headers added'); 22 | response.setHeader('header3', 'header3'); 23 | response.write('

Hello there!

'); 24 | response.write('

From port:' + server.port + '

'); 25 | response.write('

Name:

'); 26 | response.close(); 27 | }); 28 | console.log('Ending'); 29 | 30 | 31 | 32 | /* 33 | var page = require('webpage').create(); 34 | var service = server.listen(8080, function(request, response) { 35 | console.log('server started, opening page'); 36 | console.log({ 37 | method: request.method, 38 | url: request.url, 39 | httpVersion: request.httpVersion, 40 | headers: request.headers, 41 | post: request.post, 42 | rawPost: request.rawPost 43 | }); 44 | response.write('

Hello there!

'); 45 | response.write('

From port:' + server.port + '

'); 46 | response.write('

'); 47 | response.close(); 48 | }); 49 | 50 | trifle.wait(1000); 51 | 52 | 53 | page.open('http://localhost:8080', function(status) { 54 | console.log('page opened, status: ' + status); 55 | console.log('printing contents:'); 56 | console.log(page.plainText); 57 | phantom.exit(); 58 | }); 59 | 60 | */ -------------------------------------------------------------------------------- /test/feature/window.setInterval.js: -------------------------------------------------------------------------------- 1 |  2 | 3 | /* window.setInterval() */ 4 | 5 | console.log('Starting..'); 6 | var tries = 0; 7 | 8 | window.setInterval(function() { 9 | tries++; 10 | console.log('Tries..' + tries); 11 | if (tries > 9) { 12 | console.log('Finished.'); 13 | phantom.exit(); 14 | } 15 | }, 1000); 16 | -------------------------------------------------------------------------------- /test/feature/window.setTimeout.js: -------------------------------------------------------------------------------- 1 |  2 | /* window.setTimeout() */ 3 | 4 | 5 | console.log('Starting..'); 6 | 7 | window.setTimeout(function() { 8 | console.log('Finished.'); 9 | }, 1000); 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/integration/casper/TODO.txt: -------------------------------------------------------------------------------- 1 | Add CasperJS integration tests -------------------------------------------------------------------------------- /test/integration/phantom/LICENSE.BSD: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions are met: 3 | 4 | * Redistributions of source code must retain the above copyright 5 | notice, this list of conditions and the following disclaimer. 6 | * Redistributions in binary form must reproduce the above copyright 7 | notice, this list of conditions and the following disclaimer in the 8 | documentation and/or other materials provided with the distribution. 9 | * Neither the name of the nor the 10 | names of its contributors may be used to endorse or promote products 11 | derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /test/integration/phantom/README.md: -------------------------------------------------------------------------------- 1 | # [PhantomJS](http://phantomjs.org) - Scriptable Headless WebKit 2 | 3 | PhantomJS ([www.phantomjs.org](http://phantomjs.org)) is a headless WebKit scriptable with JavaScript or CoffeeScript. It is used by hundreds of [developers](https://github.com/ariya/phantomjs/wiki/Buzz) and dozens of [organizations](https://github.com/ariya/phantomjs/wiki/Users) for web-related development workflow. 4 | 5 | The latest [stable release](http://phantomjs.org/release-1.9.html) is version 1.9 (codenamed "Sakura"). Follow the official Twitter stream [@PhantomJS](http://twitter.com/PhantomJS) to get the frequent development updates. 6 | 7 | **Note**: Please **do not** create a GitHub pull request **without** reading the [Contribution Guide](https://github.com/ariya/phantomjs/blob/master/CONTRIBUTING.md) first. Failure to do so may result in the rejection of the pull request. 8 | 9 | ## Use Cases 10 | 11 | - **Headless web testing**. Lightning-fast testing without the browser is now possible! Various [test frameworks](https://github.com/ariya/phantomjs/wiki/Headless-Testing) such as Jasmine, Capybara, QUnit, Mocha, WebDriver, YUI Test, BusterJS, FuncUnit, Robot Framework, and many others are supported. 12 | - **Page automation**. [Access and manipulate](https://github.com/ariya/phantomjs/wiki/Page-Automation) web pages with the standard DOM API, or with usual libraries like jQuery. 13 | - **Screen capture**. Programmatically [capture web contents](https://github.com/ariya/phantomjs/wiki/Screen-Capture), including CSs, SVG and Canvas. Build server-side web graphics apps, from a screenshot service to a vector chart rasterizer. 14 | - **Network monitoring**. Automate performance analysis, track [page loading](https://github.com/ariya/phantomjs/wiki/Network-Monitoring) and export as standard HAR format. 15 | 16 | ## Features 17 | 18 | - **Multiplatform**, available on major operating systems: Windows, Mac OS X, Linux, other Unices. 19 | - **Fast and native implementation** of web standards: DOM, CSS, JavaScript, Canvas, SVG. No emulation! 20 | - **Pure headless (no X11) on Linux**, ideal for continuous integration systems. Also runs on Amazon EC2, Heroku, Iron.io. 21 | - **Easy to install**: [Download](http://phantomjs.org/download.html), unpack, and start having fun in just 5 minutes. 22 | 23 | ## Ecosystem 24 | 25 | PhantomJS needs not be used only as a stand-alone tool. Check also some excellent related projects: 26 | 27 | - [CasperJS](http://casperjs.org) enables easy navigation scripting and common high-level testing. 28 | - [Poltergeist](https://github.com/jonleighton/poltergeist) allows running Capybara tests headlessly. 29 | - [Guard::Jasmine](https://github.com/netzpirat/guard-jasmine) automatically tests Jasmine specs on Rails when files are modified. 30 | - [GhostDriver](http://github.com/detro/ghostdriver/) complements Selenium tests with a PhantomJS WebDriver implementation. 31 | - [PhantomRobot](https://github.com/datakurre/phantomrobot) runs Robot Framework acceptance tests in the background via PhantomJS. 32 | - [Mocha-PhantomJS](https://github.com/metaskills/mocha-phantomjs) run Mocha tests using PhantomJS. 33 | 34 | and many others [related projects](https://github.com/ariya/phantomjs/wiki/Related-Projects). 35 | 36 | ## Questions? 37 | 38 | - Explore the complete [documentation](https://github.com/ariya/phantomjs/wiki) 39 | - Read tons of [user articles](https://github.com/ariya/phantomjs/wiki/Buzz) on using PhantomJS. 40 | - Join the [mailing-list](http://groups.google.com/group/phantomjs) and discuss with other PhantomJS fans. 41 | 42 | PhantomJS is free software/open source, and is distributed under the [BSD license](http://opensource.org/licenses/BSD-3-Clause). It contains third-party code, see the included `third-party.txt` file for the license information on third-party code. 43 | 44 | PhantomJS is created and maintained by [Ariya Hidayat](http://ariya.ofilabs.com/about) (Twitter: [@ariyahidayat](http://twitter.com/ariyahidayat)), with the help of [many contributors](https://github.com/ariya/phantomjs/contributors). 45 | 46 | -------------------------------------------------------------------------------- /test/integration/phantom/examples/universe.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | // This is to be used by "module.js" (and "module.coffee") example(s). 4 | // There should NOT be a "universe.coffee" as only 1 of the 2 would 5 | // ever be loaded unless the file extension was specified. 6 | 7 | exports.answer = 42; 8 | 9 | exports.start = function () { 10 | console.log('Starting the universe....'); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/integration/phantom/examples/variable.js: -------------------------------------------------------------------------------- 1 |  2 | // This is a simple example of how to set a variable 3 | // Its used internally for testing script injection 4 | // phantom.injectJs(); 5 | 6 | var ___test190234 = [] instanceof Array; 7 | -------------------------------------------------------------------------------- /test/integration/phantom/feature/page.events.js: -------------------------------------------------------------------------------- 1 |  2 | var page = require('webpage').create(); 3 | var server = require('webserver').create(); 4 | 5 | // Start a listener to check events 6 | server.listen(8083, function(request, response) { 7 | var bodyText = JSON.stringify({ 8 | success: true, 9 | url: request.url 10 | }); 11 | response.write('eventtest9837423401' + 12 | '' + 13 | '' + bodyText + ''); 14 | response.close(); 15 | }); 16 | 17 | 18 | page.onLoadStarted = function() { 19 | console.log("LoadStarted"); 20 | } 21 | 22 | page.onLoadFinished = function(status) { 23 | console.log("LoadFinished"); 24 | } 25 | page.onNavigationRequested = function(url, type, willNavigate, main) { 26 | console.log("NavigationRequested: ", arguments); 27 | } 28 | page.onInitialized = function () { 29 | console.log("Initialized"); 30 | } 31 | page.onCallback = function(event) { 32 | console.log('Callback: ' + event); 33 | }; 34 | page.onClosing = function (closingPage) { 35 | console.log('Closing' + closingPage.url); 36 | }; 37 | page.evaluate(function () { 38 | callPhantom('beforeopen'); 39 | }); 40 | page.open('http://localhost:8083/', function (status) { 41 | //phantom.exit(); 42 | page.open('http://localhost:8083/1', function() { 43 | page.goBack(); 44 | trifle.wait(10); 45 | page.close(); 46 | }); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /test/integration/phantom/feature/page.rendering.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | 3 | page.zoomFactor=1; 4 | 5 | page.onLoadFinished = function(e) { 6 | setTimeout(function(){ 7 | page.render('msn.jpg',{quality:50}); 8 | phantom.exit();},30000); 9 | }; 10 | page.viewportSize={width:1420,height:800}; 11 | console.log(page.viewportSize); 12 | page.localT1oRemoteUrlAccessEnabled =true; 13 | 14 | setTimeout(page.open('http://www.msn.com', function() {}),30000); -------------------------------------------------------------------------------- /test/integration/phantom/phantomjs.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/test/integration/phantom/phantomjs.exe -------------------------------------------------------------------------------- /test/integration/phantom/run.bat: -------------------------------------------------------------------------------- 1 | phantomjs.exe run.js -------------------------------------------------------------------------------- /test/integration/phantom/run.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * run.js 4 | * 5 | * Initializes environment and runs triflejs unit tests 6 | * in phantomjs executable. 7 | * 8 | */ 9 | 10 | console.log('============================='); 11 | console.log('PhantomJS Benchmark Tests'); 12 | console.log('============================='); 13 | 14 | 15 | (function Stubs() { 16 | 17 | trifle = { 18 | wait : function(ms) { 19 | var date = new Date() 20 | var page = phantom.page; 21 | do { 22 | // To emulate trifle.wait() we have 23 | // to clear the event queue while waiting 24 | // for timer to reach destination. 25 | // This can be accomplished using page.sendEvent() 26 | // which in turn clears the queue for us 27 | // with a call to QApplication::processEvents(); 28 | // @see http://qt-project.org/wiki/ThreadsEventsQObjects#7494f2b4836907fc1c09311e3a0305e6 29 | // @see https://github.com/ariya/phantomjs/blob/1.9/src/webpage.cpp#L1394 30 | page.sendEvent('mousemove'); 31 | } while (new Date() < new Date(date.getTime() + ms)) 32 | } 33 | }; 34 | 35 | console.API = { 36 | color: function(name, msg) { 37 | console.log(msg); 38 | } 39 | }; 40 | 41 | })(); 42 | 43 | 44 | // Add TrifleJS unit testing tools 45 | phantom.injectJs('../../unit/tools.js'); 46 | 47 | // Add TrifleJS unit testing spec 48 | //phantom.injectJs('../../unit/spec/env.js'); 49 | //phantom.injectJs('../../unit/spec/require.js'); 50 | //phantom.injectJs('../../unit/spec/phantom.js'); 51 | //phantom.injectJs('../../unit/spec/fs.js'); 52 | //phantom.injectJs('../../unit/spec/webserver.js'); 53 | phantom.injectJs('../../unit/spec/webpage.js'); 54 | 55 | // Finish off & Report 56 | phantom.injectJs('../../unit/finish.js'); 57 | 58 | -------------------------------------------------------------------------------- /test/lib/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2011 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /test/lib/jasmine-console.js: -------------------------------------------------------------------------------- 1 | jasmine.ConsoleReporter = function(print, doneCallback, showColors) { 2 | //inspired by mhevery's jasmine-node reporter 3 | //https://github.com/mhevery/jasmine-node 4 | 5 | doneCallback = doneCallback || function() { }; 6 | 7 | var ansi = { 8 | green: '\033[32m', 9 | red: '\033[31m', 10 | yellow: '\033[33m', 11 | none: '\033[0m' 12 | }, 13 | language = { 14 | spec: "spec", 15 | failure: "failure" 16 | }; 17 | 18 | function coloredStr(color, str) { 19 | return showColors ? (ansi[color] + str + ansi.none) : str; 20 | } 21 | 22 | function greenStr(str) { 23 | return coloredStr("green", str); 24 | } 25 | 26 | function redStr(str) { 27 | return coloredStr("red", str); 28 | } 29 | 30 | function yellowStr(str) { 31 | return coloredStr("yellow", str); 32 | } 33 | 34 | function newline() { 35 | print("\n"); 36 | } 37 | 38 | function started() { 39 | print("Started"); 40 | newline(); 41 | } 42 | 43 | function greenPass() { 44 | print(greenStr("PASS")); 45 | } 46 | 47 | function redFail() { 48 | print(redStr("FAIL")); 49 | } 50 | 51 | function yellowStar() { 52 | print(yellowStr("*")); 53 | } 54 | 55 | function plural(str, count) { 56 | return count == 1 ? str : str + "s"; 57 | } 58 | 59 | function repeat(thing, times) { 60 | var arr = []; 61 | for (var i = 0; i < times; i++) { 62 | arr.push(thing); 63 | } 64 | return arr; 65 | } 66 | 67 | function indent(str, spaces) { 68 | var lines = (str || '').split("\n"); 69 | var newArr = []; 70 | for (var i = 0; i < lines.length; i++) { 71 | newArr.push(repeat(" ", spaces).join("") + lines[i]); 72 | } 73 | return newArr.join("\n"); 74 | } 75 | 76 | function specFailureDetails(suiteDescription, specDescription, stackTraces) { 77 | newline(); 78 | print(suiteDescription + " " + specDescription); 79 | for (var i = 0; i < stackTraces.length; i++) { 80 | print(indent(stackTraces[i], 2)); 81 | } 82 | } 83 | 84 | function finished(elapsed) { 85 | newline(); 86 | print("Finished in " + elapsed / 1000 + " seconds"); 87 | } 88 | 89 | function summary(colorF, specs, failed) { 90 | newline(); 91 | print(colorF(specs + " " + plural(language.spec, specs) + ", " + 92 | failed + " " + plural(language.failure, failed))); 93 | } 94 | 95 | function greenSummary(specs, failed) { 96 | summary(greenStr, specs, failed); 97 | } 98 | 99 | function redSummary(specs, failed) { 100 | summary(redStr, specs, failed); 101 | } 102 | 103 | function fullSuiteDescription(suite) { 104 | var fullDescription = suite.description; 105 | if (suite.parentSuite) fullDescription = fullSuiteDescription(suite.parentSuite) + " " + fullDescription; 106 | return fullDescription; 107 | } 108 | 109 | this.now = function() { 110 | return new Date().getTime(); 111 | }; 112 | 113 | this.reportRunnerStarting = function() { 114 | this.runnerStartTime = this.now(); 115 | started(); 116 | }; 117 | 118 | this.reportSpecStarting = function() { /* do nothing */ 119 | }; 120 | 121 | this.reportSpecResults = function(spec) { 122 | var results = spec.results(); 123 | if (results.skipped) { 124 | yellowStar(); 125 | } else { 126 | if (results.passed()) { 127 | print('#' + spec.id + ' ' + spec.suite.description + ': ' + spec.description); 128 | greenPass(); 129 | } else { 130 | print(redStr('#' + spec.id + ' ' + spec.suite.description + ': ' + spec.description)); 131 | redFail(); 132 | } 133 | } 134 | }; 135 | 136 | this.suiteResults = []; 137 | 138 | this.reportSuiteResults = function(suite) { 139 | var suiteResult = { 140 | description: fullSuiteDescription(suite), 141 | failedSpecResults: [] 142 | }; 143 | 144 | suite.results().items_.forEach(function(spec) { 145 | if (spec.failedCount > 0 && spec.description) suiteResult.failedSpecResults.push(spec); 146 | }); 147 | 148 | this.suiteResults.push(suiteResult); 149 | }; 150 | 151 | function eachSpecFailure(suiteResults, callback) { 152 | for (var i = 0; i < suiteResults.length; i++) { 153 | var suiteResult = suiteResults[i]; 154 | for (var j = 0; j < suiteResult.failedSpecResults.length; j++) { 155 | var failedSpecResult = suiteResult.failedSpecResults[j]; 156 | var stackTraces = []; 157 | for (var k = 0; k < failedSpecResult.items_.length; k++) stackTraces.push(failedSpecResult.items_[k].trace.stack); 158 | callback(suiteResult.description, failedSpecResult.description, stackTraces); 159 | } 160 | } 161 | } 162 | 163 | this.reportRunnerResults = function(runner) { 164 | eachSpecFailure(this.suiteResults, function(suiteDescription, specDescription, stackTraces) { 165 | specFailureDetails(suiteDescription, specDescription, stackTraces); 166 | }); 167 | 168 | finished(this.now() - this.runnerStartTime); 169 | 170 | var results = runner.results(); 171 | var summaryFunction = results.failedCount === 0 ? greenSummary : redSummary; 172 | summaryFunction(runner.specs().length, results.failedCount); 173 | doneCallback(runner); 174 | }; 175 | }; -------------------------------------------------------------------------------- /test/ssl/CertEnroll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Security.Cryptography.X509Certificates; 4 | using CERTENROLLLib; 5 | 6 | namespace TrifleJS.Test.SSL 7 | { 8 | /// 9 | /// Internal class used to generate self-signed SSL certificates for testing. 10 | /// If it does not work, try alternatives: 11 | /// - http://blogs.msdn.com/b/dcook/archive/2008/11/25/creating-a-self-signed-certificate-in-c.aspx 12 | /// - Use 'makecert.exe' and 'netsh http add sslcert' as shown in 'prepare.ssl.bat.example' 13 | /// 14 | internal class CertEnroll 15 | { 16 | // http://stackoverflow.com/questions/24098673/http-add-sslcert-fails-when-done-programatically 17 | internal static X509Certificate2 Generate() 18 | { 19 | try 20 | { 21 | X509Certificate2 cert = CreateSelfSignedCertificate("localhost"); 22 | 23 | var store = new X509Store(StoreName.My, StoreLocation.LocalMachine); 24 | store.Open(OpenFlags.ReadWrite); 25 | if (!store.Certificates.Contains(cert)) 26 | { 27 | store.Add(cert); 28 | } 29 | return cert; 30 | } 31 | catch { } 32 | return null; 33 | } 34 | 35 | internal static void Register(X509Certificate2 cert, int port) { 36 | if (HttpApi.IsSslRegistered(port)) throw new Exception(String.Format("Port {0} already has SSL certificate for TrifleJS!")); 37 | string appId = Native.Methods.GetAssemblyGuid(); 38 | HttpApi.BindCertificate(port, cert, StoreName.My, appId); 39 | } 40 | 41 | internal static void GenerateAndRegister(int port) { 42 | Register(Generate(), port); 43 | } 44 | 45 | // http://stackoverflow.com/questions/13806299/how-to-create-a-self-signed-certificate-using-c 46 | private static X509Certificate2 CreateSelfSignedCertificate(string subjectName) 47 | { 48 | // create DN for subject and issuer 49 | var dn = new CX500DistinguishedName(); 50 | dn.Encode("CN=" + subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE); 51 | 52 | // create a new private key for the certificate 53 | CX509PrivateKey privateKey = new CX509PrivateKey(); 54 | privateKey.ProviderName = "Microsoft Base Cryptographic Provider v1.0"; 55 | privateKey.MachineContext = true; 56 | privateKey.Length = 2048; 57 | privateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE; // use is not limited 58 | privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG; 59 | privateKey.Create(); 60 | 61 | // Use the stronger SHA512 hashing algorithm 62 | var hashobj = new CObjectId(); 63 | hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, 64 | ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, 65 | AlgorithmFlags.AlgorithmFlagsNone, "SHA512"); 66 | 67 | // add extended key usage if you want - look at MSDN for a list of possible OIDs 68 | var oid = new CObjectId(); 69 | oid.InitializeFromValue("1.3.6.1.5.5.7.3.1"); // SSL server 70 | var oidlist = new CObjectIds(); 71 | oidlist.Add(oid); 72 | var eku = new CX509ExtensionEnhancedKeyUsage(); 73 | eku.InitializeEncode(oidlist); 74 | 75 | // Create the self signing request 76 | var cert = new CX509CertificateRequestCertificate(); 77 | cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, ""); 78 | cert.Subject = dn; 79 | cert.Issuer = dn; // the issuer and the subject are the same 80 | cert.NotBefore = DateTime.Now; 81 | // this cert expires immediately. Change to whatever makes sense for you 82 | cert.NotAfter = DateTime.Now; 83 | cert.X509Extensions.Add((CX509Extension)eku); // add the EKU 84 | cert.HashAlgorithm = hashobj; // Specify the hashing algorithm 85 | cert.Encode(); // encode the certificate 86 | 87 | // Do the final enrollment process 88 | var enroll = new CX509Enrollment(); 89 | enroll.InitializeFromRequest(cert); // load the certificate 90 | enroll.CertificateFriendlyName = subjectName; // Optional: add a friendly name 91 | string csr = enroll.CreateRequest(EncodingType.XCN_CRYPT_STRING_BASE64); // Output the request in base64 92 | // and install it back as the response 93 | enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate, 94 | csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); // no password 95 | // output a base64 encoded PKCS#12 so we can import it back to the .Net security classes 96 | var base64encoded = enroll.CreatePFX("", // no password, this is for internal consumption 97 | PFXExportOptions.PFXExportChainWithRoot, EncodingType.XCN_CRYPT_STRING_BASE64); 98 | 99 | // instantiate the target class with the PKCS#12 data (and the empty password) 100 | return new System.Security.Cryptography.X509Certificates.X509Certificate2( 101 | System.Convert.FromBase64String(base64encoded), "", 102 | // mark the private key as exportable (this is usually what you want to do) 103 | System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable 104 | ); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /test/ssl/CertInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | using System.Security.Permissions; 4 | using System.IO; 5 | using System.Security.Cryptography.X509Certificates; 6 | 7 | /// 8 | /// CertInfo.exe 9 | /// -------------- 10 | /// Command line tool to get thumbprint from SSL certificate. 11 | /// Used to speed up/automate SSL self-signed certification 12 | /// using 'makecert.exe' and 'netsh http add sslcert' 13 | /// as the 'netsh' command uses a thumbprint as input. 14 | /// 15 | class CertInfo 16 | { 17 | //Reads a file. 18 | internal static byte[] ReadFile(string fileName) 19 | { 20 | FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read); 21 | int size = (int)f.Length; 22 | byte[] data = new byte[size]; 23 | size = f.Read(data, 0, size); 24 | f.Close(); 25 | return data; 26 | } 27 | //Main method begins here. 28 | static void Main(string[] args) 29 | { 30 | //Test for correct number of arguments. 31 | if (args.Length < 1) 32 | { 33 | Console.WriteLine("Usage: CertInfo "); 34 | return; 35 | } 36 | try 37 | { 38 | X509Certificate2 x509 = new X509Certificate2(); 39 | //Create X509Certificate2 object from .cer file. 40 | byte[] rawData = ReadFile(args[0]); 41 | 42 | x509.Import(rawData); 43 | 44 | //Print to console information contained in the certificate. 45 | Console.WriteLine(x509.Thumbprint); 46 | 47 | //Add the certificate to a X509Store. 48 | X509Store store = new X509Store(); 49 | store.Open(OpenFlags.MaxAllowed); 50 | store.Add(x509); 51 | store.Close(); 52 | } 53 | 54 | catch (DirectoryNotFoundException) 55 | { 56 | Console.WriteLine("Error: The directory specified could not be found."); 57 | } 58 | catch (IOException) 59 | { 60 | Console.WriteLine("Error: A file in the directory could not be accessed."); 61 | } 62 | catch (NullReferenceException) 63 | { 64 | Console.WriteLine("File must be a .cer file. Program does not have access to that type of file."); 65 | } 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /test/ssl/CertInfo.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sdesalas/trifleJS/363fc3d3f168a261be2deb1f664017e0ec43bf8e/test/ssl/CertInfo.exe -------------------------------------------------------------------------------- /test/ssl/prepare.ssl.bat.example: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: Step 1: Create a Self-Signed certificate 4 | ..\..\Build\Tools\makecert.exe -r -a sha1 -n CN=localhost -sky exchange -pe -b 01/01/2010 -e 01/01/2050 -ss my -sr localmachine ..\..\test\ssl\triflejs.test.ssl.cer 5 | 6 | :: Step 2: Get Thumbprint 7 | CertInfo triflejs.test.ssl.cer 8 | 9 | :: Step 3: Register SSL certificate 10 | netsh http add sslcert 0.0.0.0:444 certhash=00112233AABBCCDDEEFF appid={00112233-4455-6677-8899-AABBCCDDEEFF} -------------------------------------------------------------------------------- /test/unit/finish.js: -------------------------------------------------------------------------------- 1 |  2 | console.log(); 3 | console.log('-------------------------------'); 4 | console.log(' TESTING COMPLETED'); 5 | console.log('-------------------------------'); 6 | console.log('Total Tests: ' + assert.count); 7 | 8 | // ANY ERRORS? 9 | if (assert.fail.count) { 10 | console.API.color('red', 'Total Failed: ' + assert.fail.count); 11 | console.log(); 12 | // RETURN ERROR 13 | phantom.exit(assert.fail.count); 14 | } 15 | else { 16 | // ALL GOOD! 17 | console.API.color('green', 'Total Passed: ' + assert.pass.count); 18 | console.log(); 19 | phantom.exit(0); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /test/unit/phantom/run-jasmine.js: -------------------------------------------------------------------------------- 1 |  2 | // Launch tests 3 | var jasmineEnv = jasmine.getEnv(); 4 | 5 | // Add a ConsoleReporter to 6 | // 1) print with colors on the console 7 | // 2) exit when finished 8 | jasmineEnv.addReporter(new jasmine.ConsoleReporter(function(msg) { 9 | // Apply color 10 | var ansi = { 11 | green: '\033[32m', 12 | red: '\033[31m', 13 | yellow: '\033[33m', 14 | none: '\033[0m', 15 | newline: '\n' 16 | }; 17 | msg = msg.replace(ansi.newline, '').replace(ansi.none, ''); 18 | var printInColor = function(color, message) { 19 | if (color && message && ansi[color]) { 20 | console.API.color(color, message.replace(ansi[color], '')); 21 | } 22 | } 23 | // Print messages straight to console 24 | if (msg.indexOf(ansi.red) === 0) { 25 | printInColor('red', msg); 26 | } else if (msg.indexOf(ansi.yellow) === 0) { 27 | printInColor('yellow', msg); 28 | } else if (msg.indexOf(ansi.green) === 0) { 29 | printInColor('green', msg); 30 | } else { 31 | console.log(msg); 32 | } 33 | }, function(reporter) { 34 | // On complete 35 | phantom.exit(reporter.results().failedCount); 36 | }, true)); 37 | 38 | // Launch tests 39 | jasmineEnv.updateInterval = 500; 40 | jasmineEnv.execute(); 41 | 42 | -------------------------------------------------------------------------------- /test/unit/phantom/run-tests.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the PhantomJS project from Ofi Labs. 3 | 4 | Copyright (C) 2011 Ariya Hidayat 5 | Copyright (C) 2011 Ivan De Marino 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | * Neither the name of the nor the 16 | names of its contributors may be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | // Load Jasmine and the HTML reporter 32 | phantom.injectJs("./lib/jasmine.js"); 33 | phantom.injectJs("./lib/jasmine-console.js"); 34 | phantom.injectJs("./lib/chai.js"); 35 | 36 | var should = chai.should(); 37 | 38 | // Helper funcs 39 | function expectHasFunction(o, name) { 40 | it("should have '" + name + "' function", function() { 41 | expect(typeof o[name]).toEqual('function'); 42 | }); 43 | } 44 | 45 | function expectHasProperty(o, name) { 46 | it("should have '" + name + "' property", function() { 47 | expect(o.hasOwnProperty(name)).toBeTruthy(); 48 | }); 49 | } 50 | 51 | function expectHasPropertyString(o, name) { 52 | expectHasProperty(o, name); 53 | 54 | it("should have '" + name + "' as a string", function() { 55 | expect(typeof o[name]).toEqual('string'); 56 | }); 57 | } 58 | 59 | // Setting the "working directory" to the "/test" directory 60 | var fs = require('fs'); 61 | 62 | fs.changeWorkingDirectory(phantom.libraryPath); 63 | 64 | // Remove xdebug 65 | console.xdebug = function() { }; 66 | 67 | // Load specs 68 | phantom.injectJs("./spec/phantom.js"); 69 | 70 | // Execute! 71 | phantom.injectJS("run-jasmine.js"); 72 | -------------------------------------------------------------------------------- /test/unit/phantom/scripts/frametest-harness.js: -------------------------------------------------------------------------------- 1 |  2 | var page = require('webpage').create(); 3 | var server = require('webserver').create(); 4 | var fs = require('fs'); 5 | 6 | fs.changeWorkingDirectory('E:\\PROJECTS\\GITHUB\\trifleJS\\bin\\Debug\\'); 7 | 8 | server.listen(8897, function(request, response) { 9 | console.log('requested url--> ' + request.url); 10 | var path = request.url; 11 | if (!path || path === '/') path = '/index.html'; 12 | response.write(fs.read('test/frames' + path)); 13 | response.close(); 14 | }); 15 | 16 | -------------------------------------------------------------------------------- /test/unit/phantom/scripts/frametest.js: -------------------------------------------------------------------------------- 1 | 2 | var page = require('webpage').create(); 3 | 4 | page.open('http://localhost:8897', function(status) { 5 | 6 | console.log(status); 7 | page.windowName = "main frame"; 8 | console.log(JSON.stringify({ 9 | "page.framesCount": page.framesCount, 10 | "page.framesName": page.framesName, 11 | "page.windowName": page.windowName, 12 | "page.frameName": page.frameName, 13 | "page.frameUrl": page.frameUrl, 14 | "page.focusedFrameName": page.focusedFrameName 15 | })); 16 | page.switchToFrame('frame1'); 17 | console.log('------'); 18 | 19 | console.log(JSON.stringify({ 20 | "page.framesCount": page.framesCount, 21 | "page.framesName": page.framesName, 22 | "page.windowName": page.windowName, 23 | "page.frameName": page.frameName, 24 | "page.frameUrl": page.frameUrl, 25 | "page.focusedFrameName": page.focusedFrameName 26 | })); 27 | page.switchToMainFrame(); 28 | page.switchToFrame('frame2-1'); 29 | console.log('------'); 30 | 31 | console.log(JSON.stringify({ 32 | "page.framesCount": page.framesCount, 33 | "page.framesName": page.framesName, 34 | "page.windowName": page.windowName, 35 | "page.frameName": page.frameName, 36 | "page.frameUrl": page.frameUrl, 37 | "page.focusedFrameName": page.focusedFrameName 38 | })); 39 | phantom.exit(); 40 | }) -------------------------------------------------------------------------------- /test/unit/phantom/spec/phantom.js: -------------------------------------------------------------------------------- 1 | describe("phantom global object", function() { 2 | it("should exist", function() { 3 | expect(typeof phantom).toEqual('object'); 4 | }); 5 | 6 | it("should have args property", function() { 7 | expect(phantom.hasOwnProperty('args')).toBeTruthy(); 8 | }); 9 | 10 | it("should have args as an array", function() { 11 | expect(typeof phantom.args).toEqual('object'); 12 | }); 13 | 14 | it("should have libraryPath property", function() { 15 | expect(phantom.hasOwnProperty('libraryPath')).toBeTruthy(); 16 | }); 17 | 18 | it("should have libraryPath as a string", function() { 19 | expect(typeof phantom.libraryPath).toEqual('string'); 20 | }); 21 | 22 | it("should not have an empty libraryPath", function() { 23 | expect(phantom.libraryPath.length).toNotEqual(0); 24 | }); 25 | 26 | it("should have scriptName property", function() { 27 | expect(phantom.hasOwnProperty('scriptName')).toBeTruthy(); 28 | }); 29 | 30 | it("should have scriptName as a string", function() { 31 | expect(typeof phantom.scriptName).toEqual('string'); 32 | }); 33 | 34 | it("should not have an empty scriptName", function() { 35 | expect(phantom.scriptName.length).toNotEqual(0); 36 | }); 37 | 38 | it("should have outputEncoding property", function() { 39 | expect(phantom.hasOwnProperty('outputEncoding')).toBeTruthy(); 40 | }); 41 | 42 | it("should have the default outputEncoding of UTF-8", function() { 43 | expect(phantom.outputEncoding.toLowerCase()).toEqual('utf-8'); 44 | }); 45 | 46 | it("should have version property", function() { 47 | expect(phantom.hasOwnProperty('version')).toBeTruthy(); 48 | }); 49 | 50 | it("should return 1 as the major version", function() { 51 | expect(phantom.version.major).toEqual(1); 52 | }); 53 | 54 | it("should return 7 as the minor version", function() { 55 | expect(phantom.version.minor).toEqual(7); 56 | }); 57 | 58 | it("should return 0 as the patch version", function() { 59 | expect(phantom.version.patch).toEqual(0); 60 | }); 61 | 62 | it("should have 'injectJs' function", function() { 63 | expect(typeof phantom.injectJs).toEqual("function"); 64 | }); 65 | 66 | it("should have 'exit' function", function() { 67 | expect(typeof phantom.exit).toEqual("function"); 68 | }); 69 | 70 | it("should have 'cookiesEnabled' property, and should be 'true' by default", function() { 71 | expect(phantom.hasOwnProperty('cookiesEnabled')).toBeTruthy(); 72 | expect(phantom.cookiesEnabled).toBeTruthy(); 73 | }); 74 | 75 | // it("should be able to get the error signal handler that is currently set on it", function() { 76 | // phantom.onError = undefined; 77 | // expect(phantom.onError).toBeUndefined(); 78 | // var onErrorFunc1 = function() { return !"x"; }; 79 | // phantom.onError = onErrorFunc1; 80 | // expect(phantom.onError).toEqual(onErrorFunc1); 81 | // var onErrorFunc2 = function() { return !!"y"; }; 82 | // phantom.onError = onErrorFunc2; 83 | // expect(phantom.onError).toEqual(onErrorFunc2); 84 | // expect(phantom.onError).toNotEqual(onErrorFunc1); 85 | // phantom.onError = null; 86 | // // Will only allow setting to a function value, so setting it to `null` returns `undefined` 87 | // expect(phantom.onError).toBeUndefined(); 88 | // phantom.onError = undefined; 89 | // expect(phantom.onError).toBeUndefined(); 90 | // }); 91 | 92 | // it("reports parse time error source and line in stack", function() { 93 | // var stack; 94 | // phantom.onError = function(message, s) { stack = s; }; 95 | 96 | // var helperFile = "./fixtures/parse-error-helper.js"; 97 | // phantom.injectJs(helperFile); 98 | 99 | // waits(0); 100 | 101 | // runs(function() { 102 | // expect(stack[0].file).toEqual(helperFile); 103 | // expect(stack[0].line).toEqual(2); 104 | // phantom.onError = phantom.defaultErrorHandler; 105 | // }); 106 | // }); 107 | }); 108 | -------------------------------------------------------------------------------- /test/unit/phantom/spec/webserver.js: -------------------------------------------------------------------------------- 1 | describe("WebServer constructor", function() { 2 | it("should not exist in window", function() { 3 | expect(window.hasOwnProperty('WebServer')).toBeFalsy(); 4 | }); 5 | 6 | it("should be a function", function() { 7 | var WebServer = require('webserver').create; 8 | expect(typeof WebServer).toEqual('function'); 9 | }); 10 | }); 11 | 12 | var expectedPostData = false, expectedBinaryData = false; 13 | 14 | function checkRequest(request, response) { 15 | console.log('checking request...'); 16 | expect(typeof request).toEqual('object'); 17 | expect(request.hasOwnProperty('url')).toBeTruthy(); 18 | expect(request.hasOwnProperty('method')).toBeTruthy(); 19 | expect(request.hasOwnProperty('httpVersion')).toBeTruthy(); 20 | expect(request.hasOwnProperty('headers')).toBeTruthy(); 21 | expect(typeof request.headers).toEqual('object'); 22 | 23 | expect(typeof response).toEqual('object'); 24 | expect(response.hasOwnProperty('statusCode')).toBeTruthy(); 25 | expect(response.hasOwnProperty('headers')).toBeTruthy(); 26 | //expect(typeof response['setHeaders']).toEqual('function'); 27 | expect(typeof response['setHeader']).toEqual('function'); 28 | expect(typeof response['header']).toEqual('function'); 29 | expect(typeof response['write']).toEqual('function'); 30 | expect(typeof response['writeHead']).toEqual('function'); 31 | 32 | if (expectedPostData !== false) { 33 | expect(request.method).toEqual("POST"); 34 | expect(request.hasOwnProperty('post')).toBeTruthy(); 35 | console.log("request.post => " + JSON.stringify(request.post, null, 4)); 36 | console.log("expectedPostData => " + JSON.stringify(expectedPostData, null, 4)); 37 | console.log("request.headers => " + JSON.stringify(request.headers, null, 4)); 38 | if (request.headers["Content-Type"] && request.headers["Content-Type"] === "application/x-www-form-urlencoded") { 39 | expect(typeof request.post).toEqual('object'); 40 | expect(request.post).toEqual(expectedPostData); 41 | expect(request.postRaw).toBeTruthy(); 42 | expect(typeof request.postRaw).toEqual('string'); 43 | } else { 44 | expect(typeof request.post).toEqual('string'); 45 | expect(request.post).toNotEqual(expectedPostData); 46 | expect(request.postRaw).toBeFalsy(); 47 | } 48 | expectedPostData = false; 49 | } 50 | 51 | if (expectedBinaryData !== false) { 52 | response.setEncoding('binary'); 53 | response.write(expectedBinaryData); 54 | expectedBinaryData = false; 55 | } else { 56 | response.write("request handled"); 57 | } 58 | response.close(); 59 | } 60 | 61 | describe("WebServer object", function() { 62 | var server = require('webserver').create(); 63 | 64 | it("should be creatable", function() { 65 | expect(typeof server).toEqual('object'); 66 | expect(server).toNotEqual(null); 67 | }); 68 | 69 | it("should have objectName as 'WebServer'", function() { 70 | expect(server.objectName).toEqual('WebServer'); 71 | }); 72 | 73 | expectHasProperty(server, 'port'); 74 | it("should have port as string", function() { 75 | expect(typeof server.port).toEqual('string'); 76 | }); 77 | it("should not listen to any port by default", function() { 78 | expect(server.port).toEqual(""); 79 | }); 80 | 81 | expectHasFunction(server, 'listen'); 82 | expectHasFunction(server, 'close'); 83 | 84 | it("should fail to listen to blocked ports", function() { 85 | //NOTE: is this really blocked everywhere? 86 | expect(server.listen(80, function(){})).toEqual(false); 87 | expect(server.port).toEqual(""); 88 | }); 89 | it("should be able to listen to some port", function() { 90 | //NOTE: this can fail if the port is already being listend on... 91 | expect(server.listen("12345", checkRequest)).toEqual(true); 92 | expect(server.port).toEqual("12345"); 93 | }); 94 | 95 | it("should handle requests", function() { 96 | console.debug("should handle requests.."); 97 | var page = require('webpage').create(); 98 | var url = "http://localhost:12345/foo/bar.php?asdf=true"; 99 | var handled = false; 100 | runs(function() { 101 | expect(handled).toEqual(false); 102 | page.open(url, function (status) { 103 | expect(status == 'success').toEqual(true); 104 | expect(page.plainText).toEqual("request handled"); 105 | handled = true; 106 | }); 107 | }); 108 | 109 | waits(50); 110 | 111 | runs(function() { 112 | expect(handled).toEqual(true); 113 | }); 114 | }); 115 | 116 | it("should handle post requests ('Content-Type' = 'application/x-www-form-urlencoded')", function() { 117 | var page = require('webpage').create(); 118 | var url = "http://localhost:12345/foo/bar.txt?asdf=true"; 119 | //note: sorted by key (map) 120 | expectedPostData = {'answer' : "42", 'universe' : "expanding"}; 121 | var handled = false; 122 | runs(function() { 123 | expect(handled).toEqual(false); 124 | page.open(url, 'post', "universe=expanding&answer=42", { "Content-Type" : "application/x-www-form-urlencoded" }, function (status) { 125 | expect(status == 'success').toEqual(true); 126 | expect(page.plainText).toEqual("request handled"); 127 | handled = true; 128 | }); 129 | }); 130 | 131 | waits(50); 132 | 133 | runs(function() { 134 | expect(handled).toEqual(true); 135 | }); 136 | }); 137 | 138 | it("should handle post requests ('Content-Type' = 'ANY')", function() { 139 | var page = require('webpage').create(); 140 | var url = "http://localhost:12345/foo/bar.txt?asdf=true"; 141 | //note: sorted by key (map) 142 | expectedPostData = {'answer' : "42", 'universe' : "expanding"}; 143 | var handled = false; 144 | runs(function() { 145 | expect(handled).toEqual(false); 146 | page.open(url, 'post', "universe=expanding&answer=42", { "Content-Type" : "application/json;charset=UTF-8" }, function (status) { 147 | expect(status == 'success').toEqual(true); 148 | expect(page.plainText).toEqual("request handled"); 149 | handled = true; 150 | }); 151 | }); 152 | 153 | waits(50); 154 | 155 | runs(function() { 156 | expect(handled).toEqual(true); 157 | }); 158 | }); 159 | 160 | it("should handle binary data", function() { 161 | var page = require('webpage').create(); 162 | var url = "http://localhost:12345/"; 163 | var fs = require('fs'); 164 | expectedBinaryData = fs.read('phantomjs.png', 'b'); 165 | var handled = false; 166 | runs(function() { 167 | expect(handled).toEqual(false); 168 | page.open(url, 'get', function(status) { 169 | expect(status == 'success').toEqual(true); 170 | function checkImg() { 171 | var img = document.querySelector('img'); 172 | return (img) && (img.width == 200) && (img.height == 200); 173 | } 174 | expect(page.evaluate(checkImg)).toEqual(true); 175 | handled = true; 176 | }); 177 | }); 178 | 179 | waits(50); 180 | 181 | runs(function() { 182 | expect(handled).toEqual(true); 183 | }); 184 | }); 185 | 186 | }); -------------------------------------------------------------------------------- /test/unit/phantom/tools.js: -------------------------------------------------------------------------------- 1 |  2 | // Add Helper Functions 3 | function expectHasFunction(o, name) { 4 | it("should have '" + name + "' function", function() { 5 | expect(typeof o[name]).toEqual('function'); 6 | }); 7 | } 8 | 9 | function expectHasProperty(o, name) { 10 | it("should have '" + name + "' property", function() { 11 | expect(o.hasOwnProperty(name)).toBeTruthy(); 12 | }); 13 | } 14 | 15 | function expectHasPropertyString(o, name) { 16 | expectHasProperty(o, name); 17 | 18 | it("should have '" + name + "' as a string", function() { 19 | expect(typeof o[name]).toEqual('string'); 20 | }); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /test/unit/ref/sample.html: -------------------------------------------------------------------------------- 1 |  2 | 3 |

Hello there!

4 |

From port:[[PORT]]

5 |

6 |

7 | 8 | 9 |

10 | 11 |
12 |

13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/unit/ref/sample_module.js: -------------------------------------------------------------------------------- 1 |  2 | 3 | exports.ok = true; 4 | exports.module = module; 5 | exports.require = require; 6 | 7 | -------------------------------------------------------------------------------- /test/unit/ref/script.js: -------------------------------------------------------------------------------- 1 |  2 | var ___test190234 = [] instanceof Array; 3 | -------------------------------------------------------------------------------- /test/unit/spec/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | * require.js 3 | * 4 | * Runs a set of unit tests used to verify the 5 | * commonJS module functionality. 6 | * 7 | */ 8 | 9 | assert.suite('CommonJS functionality', function() { 10 | 11 | assert.section('Globals'); 12 | 13 | assert(typeof require === 'function', 'require is a function'); 14 | 15 | assert.section('Calling Inbuilt Modules'); 16 | 17 | var fs = require('fs'); 18 | assert(typeof fs === 'object', 'fs module can be instantiated'); 19 | assert(typeof fs.workingDirectory === 'string', 'fs module contains a workingDirectory') 20 | var fs2 = require('fs'); 21 | assert(typeof fs2 === 'object', 'fs module can be instantiated a second time'); 22 | assert(fs2 === fs, 'fs module returns the same object when instantiated a second time'); 23 | 24 | assert.section('Calling Modules using a path'); 25 | 26 | var path = 'examples/universe'; 27 | var sample_module = require(path); 28 | assert(typeof sample_module === 'object', 'module can be instantiated without an extension'); 29 | assert(!!sample_module.id, 'module has an id property'); 30 | 31 | assert(sample_module.answer === 42, 'module.answer is set to 42'); 32 | 33 | path = 'examples/universe.js'; 34 | sample_module = require(path); 35 | assert(typeof sample_module === 'object', 'module can be instantiated using a file path'); 36 | assert(sample_module.answer === 42, 'module.answer is set to 42'); 37 | 38 | var sample_module2 = require(path); 39 | 40 | assert(typeof sample_module2 === 'object', 'module can be instantiated a second time'); 41 | assert(sample_module2.answer === 42, 'module.answer is set to 42 when instantiated a second time'); 42 | assert(sample_module === sample_module2, 'module returns same object when instantiated a second time'); 43 | 44 | var sample_module3 = require('examples\\universe.js'); 45 | 46 | assert(typeof sample_module3 === 'object', 'module can be instantiated using a windows path'); 47 | assert(sample_module3.answer === 42, 'module.answer is set to 42 when instantiated using windows path'); 48 | assert(sample_module === sample_module3, 'module returns same object when instantiated using windows path'); 49 | 50 | }); -------------------------------------------------------------------------------- /test/unit/spec/ssl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ssl.js 3 | * 4 | * Runs a set of unit tests used to verify 5 | * SSL connectivity. 6 | * 7 | */ 8 | 9 | 10 | assert.suite('SSL connectivity', function() { 11 | 12 | if (sslSupport !== true) { 13 | console.warn('No SSL Support, skipping tests!'); 14 | return; 15 | } 16 | 17 | // Instantiate SSL server 18 | var serverCount = 0; 19 | var server = require('webserver').create(); 20 | server.listen('https://localhost:8043', function(request, response) { 21 | serverCount++; 22 | response.write("Hello SSL!"); 23 | response.close(); 24 | }); 25 | 26 | // -------------------------------------------- 27 | assert.section('Basic SSL Interaction (WebServer & WebPage)', function() { 28 | 29 | var clientCount = 0; 30 | var page = require('webpage').create(); 31 | 32 | // Turn errors on 33 | trifle.API.IgnoreSSLErrors = false; 34 | 35 | page.open('https://localhost:8043/1', function(status) { 36 | clientCount++; 37 | }); 38 | 39 | trifle.wait(1000); 40 | 41 | assert(serverCount === 0, 'Browser does not hit the server if we turn errors on'); 42 | assert(clientCount === 1, 'Browser executes callback if we turn errors on'); 43 | assert(page.plainText.indexOf('security certificate') > -1, 'Browser shows a certificate error page'); 44 | 45 | 46 | // Turn errors off 47 | trifle.API.IgnoreSSLErrors = true; 48 | 49 | page.open('https://localhost:8043/2', function(status) { 50 | assert.ready = true; 51 | clientCount++; 52 | }); 53 | 54 | assert.waitUntilReady(); 55 | 56 | assert(serverCount === 1, 'Browser hits the server when ignoring SSL errors'); 57 | assert(clientCount === 2, 'Browser executes callback if we turn SSL errors off'); 58 | assert(page.plainText === 'Hello SSL!', 'Correct text returned from SSL server'); 59 | 60 | }); 61 | 62 | assert.section('CLI Options', function() { 63 | 64 | 65 | 66 | }); 67 | 68 | }); -------------------------------------------------------------------------------- /test/unit/spec/system.js: -------------------------------------------------------------------------------- 1 | /* 2 | * require.js 3 | * 4 | * Runs a set of unit tests used to verify 5 | * system functionality (platform, arguments etc) 6 | * 7 | */ 8 | 9 | assert.suite('Object: system', function() { 10 | 11 | // SETUP 12 | var system = require('system'); 13 | 14 | // -------------------------------------------- 15 | assert.section('Properties & methods'); 16 | 17 | assert.checkMembers(system, 'system', { 18 | pid: 'number', 19 | platform: 'string', 20 | os: 'object', 21 | env: 'object', 22 | args: 'object' 23 | }); 24 | 25 | assert.checkMembers(system.os, 'system.os', { 26 | architecture: 'string', 27 | name: 'string', 28 | version: 'string' 29 | }); 30 | 31 | var exists = {}; 32 | 33 | Object.keys(system.env).forEach(function(key) { 34 | switch(key.toLowerCase()) { 35 | case 'path': 36 | assert(typeof system.env[key] === 'string', 'system.env "' + key + '" variable is of type string'); 37 | exists.path = true; 38 | break; 39 | case 'programfiles': 40 | assert(typeof system.env[key] === 'string', 'system.env "' + key + '" variable is of type string'); 41 | exists.programfiles = true; 42 | break; 43 | } 44 | }); 45 | 46 | assert(exists.path === true, 'system.env contains the "PATH" variable'); 47 | assert(exists.programfiles === true, 'system.env contains the "ProgramFiles" variable'); 48 | 49 | assert(system.args instanceof Array, 'system.args is an array'); 50 | assert(system.os.name === 'windows', 'system.os.name is "windows"'); 51 | 52 | if (system.env.ProgramFiles.indexOf('x86') > 0) { 53 | assert(system.os.architecture === '64bit', 'system.os.architecture is "64bit"'); 54 | } else { 55 | assert(system.os.architecture === '32bit', 'system.os.architecture is "32bit"'); 56 | } 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /test/unit/spec/webserver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * webserver.js 3 | * 4 | * Runs a set of unit tests used that check 5 | * the the functionality in the webserver module 6 | * 7 | */ 8 | 9 | assert.suite('Module: WebServer', function() { 10 | 11 | // SETUP 12 | var fs = require("fs"); 13 | var server = require('webserver').create(); 14 | var page = require('webpage').create(); 15 | var workingDirectory = fs.workingDirectory; 16 | var loadCount = 0; 17 | var helloWorldListener = function(request, response) { loadCount++; response.write("Hello World"); response.close(); } 18 | var helloWorld2Listener = function(request, response) { loadCount++; response.write("Hello World2"); response.close(); } 19 | var infoListener = function(request, response) { loadCount++; response.write(JSON.stringify({success: true, httpVersion: request.httpVersion, method: request.method, url: request.url, headers: request.headers, post: request.post, postRaw: request.postRaw})); response.close(); } 20 | 21 | // -------------------------------------------- 22 | assert.section('Instantiation'); 23 | // -------------------------------------------- 24 | 25 | assert(!!server, 'server can be instantiated using require()'); 26 | assert(typeof server === 'object', 'server is an object'); 27 | 28 | // -------------------------------------------- 29 | assert.section('Properties & methods'); 30 | // -------------------------------------------- 31 | 32 | assert(typeof server.listen === 'function', 'server.listen() is a function'); 33 | assert(typeof server.close === 'function', 'server.close() is a function'); 34 | assert(typeof server.port === 'string', 'server.port is a string'); 35 | assert(server.port === '', 'server.port is an empty string to beging with') 36 | 37 | // -------------------------------------------- 38 | assert.section('Listening for connections'); 39 | // -------------------------------------------- 40 | 41 | var requestInfo; 42 | 43 | var isListening = server.listen(8080, helloWorldListener); 44 | 45 | assert(isListening === true, 'server.listen() returns true when listening'); 46 | assert(server.port === '8080', 'server.port returns the correct port') 47 | 48 | page.open('http://localhost:8080/hello1', function(status) { 49 | assert.ready = true; 50 | }); 51 | 52 | assert.waitUntilReady(); 53 | 54 | assert(page.plainText === "Hello World", 'server responded with "Hello World" on 8080'); 55 | assert(loadCount === 1, 'listener on 8080 fired once'); 56 | 57 | // Try again on same port 58 | 59 | isListening = server.listen(8080, helloWorld2Listener); 60 | 61 | assert(isListening === true, 'server.listen() return true when listening on same port'); 62 | assert(server.port === '8080', 'server.port returns the correct port') 63 | 64 | page.open('http://localhost:8080/hello2', function(status) { 65 | assert.ready = true; 66 | }); 67 | 68 | assert.waitUntilReady(); 69 | 70 | assert(page.plainText === "Hello World2", 'server responded with "Hello World2" on 8080 (binding is replaced)'); 71 | assert(loadCount === 2, 'listener on 8080 fired once'); 72 | 73 | isListening = server.listen(8081, infoListener); 74 | 75 | assert(isListening === true, 'server.listen() return true when listening on a different port'); 76 | assert(server.port === '8081', 'server.port returns the correct port') 77 | 78 | page.open('http://localhost:8081/testurl?k=v', function(status) { 79 | assert.ready = true; 80 | }); 81 | 82 | assert.waitUntilReady(); 83 | 84 | requestInfo = JSON.parse(page.plainText); 85 | 86 | assert(loadCount === 3, 'listener on 8081 fired once'); 87 | assert(requestInfo.success === true, 'request was a success'); 88 | assert(requestInfo.httpVersion === '1.1', 'request httpVersion was 1.1'); 89 | assert(requestInfo.method === 'GET', 'request method was correct (GET)'); 90 | assert(typeof(requestInfo.headers) === 'object', 'request has some headers'); 91 | assert(requestInfo.headers.Host === 'localhost:8081', 'request has correct Origin header'); 92 | assert(requestInfo.url === '/testurl?k=v', 'request url was correct (/testurl?k=v)'); 93 | 94 | // -------------------------------------------- 95 | assert.section('POST Request'); 96 | // -------------------------------------------- 97 | 98 | page.open('http://localhost:8081/', 'POST', function(status) { 99 | assert.ready = true; 100 | }); 101 | 102 | assert.waitUntilReady(); 103 | 104 | requestInfo = JSON.parse(page.plainText); 105 | 106 | assert(loadCount === 4, 'listener on 8081 fired once'); 107 | assert(requestInfo.method === 'POST', 'request method was correct (POST)'); 108 | assert(typeof(requestInfo.headers) === 'object', 'request has some headers'); 109 | assert(requestInfo.post === '', 'request body was empty (no data was posted)'); 110 | assert(requestInfo.postRaw === '', 'request raw post was empty (no data was posted)'); 111 | 112 | page.customHeaders = { 113 | 'Content-Type': 'application/x-www-form-urlencoded' 114 | }; 115 | 116 | page.open('http://localhost:8081/', 'POST', 'user=username&pass=password&price=$15&location=o\'rileys bar&perc=10%', function(status) { 117 | assert.ready = true; 118 | }); 119 | 120 | assert.waitUntilReady(); 121 | 122 | requestInfo = JSON.parse(page.plainText); 123 | 124 | assert(loadCount === 5, 'listener on 8081 fired once'); 125 | assert(requestInfo.method === 'POST', 'request method was correct (POST)'); 126 | assert(typeof(requestInfo.headers) === 'object', 'request has some headers'); 127 | assert(requestInfo.headers['Content-Type'] === 'application/x-www-form-urlencoded', 'Content-Type header set to application/x-www-form-urlencoded'); 128 | assert(typeof(requestInfo.post) === 'object', 'request body is an object with some data'); 129 | assert(requestInfo.post.user === 'username', 'request body contains username'); 130 | assert(requestInfo.post.pass === 'password', 'request body contains password'); 131 | assert(requestInfo.post.price === '$15', 'request body contains price ($15)'); 132 | assert(requestInfo.post.location === 'o\'rileys bar', 'request body contains location (including special chars)'); 133 | assert(requestInfo.post.perc === '10%', 'request body contains perc (including percentage sign)'); 134 | assert(requestInfo.postRaw === 'user=username&pass=password&price=$15&location=o\'rileys bar&perc=10%', 'request raw post contains the data sent'); 135 | 136 | 137 | // TEARDOWN 138 | server.close() 139 | 140 | }); 141 | 142 | 143 | -------------------------------------------------------------------------------- /test/unit/tools.js: -------------------------------------------------------------------------------- 1 |  2 | var assert = function(condition, message) { 3 | assert.n++; 4 | assert.count++; 5 | if (condition !== true) { 6 | assert.fail(message); 7 | } else { 8 | assert.pass(message); 9 | } 10 | } 11 | 12 | assert.count = 0; 13 | 14 | assert.pass = function(message) { 15 | console.API.color('green', assert.n + '. PASS: ' + (message || '(no message)') + '.'); 16 | assert.pass.count++; 17 | } 18 | 19 | assert.pass.count = 0; 20 | 21 | assert.fail = function(message) { 22 | console.API.color('red', assert.n + '. FAIL: ' + (message || '(no message)') + '.'); 23 | assert.fail.count++; 24 | } 25 | 26 | assert.fail.count = 0; 27 | 28 | 29 | assert.isError = function(callback, message) { 30 | assert.n++; 31 | assert.count++; 32 | try { 33 | callback.call(this); 34 | } catch(e) { 35 | assert.pass(message); 36 | return; 37 | } 38 | assert.fail(message); 39 | } 40 | 41 | assert.checkMembers = function(obj, objName, config) { 42 | if (obj && objName && config) { 43 | if (config instanceof Array) { 44 | config.forEach(function(prop) { 45 | assert(typeof obj[prop] !== 'undefined', objName + '.' + prop + ' exists'); 46 | }); 47 | return; 48 | } else if (config instanceof Object) { 49 | Object.keys(config).forEach(function(prop) { 50 | assert(typeof obj[prop] === config[prop], objName + '.' + prop + ' exists and is of type "' + config[prop] + '"'); 51 | }); 52 | return; 53 | } 54 | } 55 | throw new Error('assert.checkMembers() received an incorrect config object'); 56 | } 57 | 58 | assert.reset = function() { 59 | assert.n = 0; 60 | } 61 | 62 | assert.suite = function(name, callback) { 63 | this.reset(); 64 | console.log(''); 65 | console.log('-------------------------------'); 66 | console.log(' ' + name) 67 | console.log('-------------------------------'); 68 | this.suitename = name; 69 | try { 70 | callback(); 71 | } catch (e) { 72 | assert.n++ 73 | assert.fail(e); 74 | } 75 | } 76 | 77 | assert.section = function(name, callback) { 78 | console.log(''); 79 | console.log(' ' + this.suitename + ' - ' + name); 80 | console.log(''); 81 | this.sectionname = name; 82 | if (typeof callback === 'function') { 83 | callback(); 84 | } 85 | } 86 | 87 | assert.waitFor = function(callback, defaultTimeout) { 88 | if (defaultTimeout) { 89 | var start = (new Date()).getTime(); 90 | do { 91 | trifle.wait(10); 92 | console.log((new Date()).getTime()); 93 | } while (!callback() && (new Date()).getTime() < (start + defaultTimeout)); 94 | } else { 95 | do { 96 | trifle.wait(10); 97 | } while (!callback()); 98 | } 99 | } 100 | 101 | assert.waitUntilReady = function() { 102 | while(assert.ready !== true) { 103 | trifle.wait(10); 104 | } 105 | assert.ready = false; 106 | } 107 | 108 | --------------------------------------------------------------------------------