引用nuget包:

注意:Geckofx45 nuget包必须是最后引用,否则初始化会出错

编写JsRunner

using Gecko;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Way.UnitTest
{
class JsRunner:IDisposable
{
static JsRunner()
{
Xpcom.Initialize("Firefox");
} static List<JsFileReference> ReferenceConfigs = new List<JsFileReference>();
static List<string> GlobalJSFiles = new List<string>();
/// <summary>
/// 设置js文件依赖
/// </summary>
/// <param name="jsFile">js文件路径</param>
/// <param name="references">所依赖的js文件路径</param>
public static void SetJsReference(string jsFile,IEnumerable<string> references)
{
ReferenceConfigs.Add(new JsFileReference() {
JsFile = jsFile,
References = references,
});
}
/// <summary>
/// 添加全局js文件
/// </summary>
/// <param name="jsFile"></param>
public static void AddGlobalJsFile(string jsFile)
{
GlobalJSFiles.Add(jsFile);
} public JsRunner()
{
JsFiles.AddRange(GlobalJSFiles);
}
~JsRunner()
{
Dispose();
}
List<string> JsFiles = new List<string>();
/// <summary>
/// 添加js文件
/// </summary>
/// <param name="jsPath">js文件路径</param>
public void AddJsFile(string jsPath)
{
putJsFileContentToList(jsPath); } void putJsFileContentToList(string jsPath)
{
if (JsFiles.Contains(jsPath) )
{
return;
}
//查找该js是否引用其他js
var arr = ReferenceConfigs.Where(m => string.Equals(m.JsFile, jsPath, StringComparison.CurrentCultureIgnoreCase));
foreach( var item in arr )
{
foreach( var path in item.References )
{
putJsFileContentToList(path);
}
}
JsFiles.Add(jsPath);
} /// <summary>
/// 运行js代码
/// </summary>
/// <typeparam name="T">返回值类型</typeparam>
/// <param name="jsCode">一段js代码。如:return data.name;</param>
/// <param name="data">传到js里面的对象,js可以通过data.*直接使用此参数</param>
/// <returns></returns>
public T Run<T>(string jsCode, object data)
{
var gecko = new GeckoWebBrowser();
gecko.CreateControl();bool loadFinished = false; gecko.NavigationError += (s, e) =>
{
};
gecko.NSSError += (s, e) =>
{
};
gecko.DocumentCompleted += (s, e) => {
loadFinished = true;
};
string tempFileName = System.IO.Path.GetTempFileName();
System.IO.StreamWriter sw = new StreamWriter(System.IO.File.OpenWrite(tempFileName)); sw.WriteLine("<!DOCTYPE html>");
sw.WriteLine("<html>");
foreach (var path in JsFiles)
{
sw.WriteLine("<script src=\"file:///" + path + "\" type=\"text/javascript\"></script>");
}
sw.WriteLine("<body>"); sw.WriteLine("<input type=hidden id='inputResult'>");
sw.WriteLine("<input type=hidden id='inputError'>");
sw.WriteLine("</body>");
sw.WriteLine("</html>");
sw.Dispose(); gecko.Navigate("file:///" + tempFileName); while (!loadFinished)
{
System.Threading.Thread.Sleep();
System.Windows.Forms.Application.DoEvents();
}
System.IO.File.Delete(tempFileName); var jsContext = new AutoJSContext(gecko.Window); var js = @"
(function(d){
try{
var result = (function(data){" + jsCode + @"})(d);
return JSON.stringify(result);
}catch(e)
{
if(typeof e == 'string')
return JSON.stringify({ ______err : e , line:0 });
else
return JSON.stringify({ ______err : e.message , line:e.lineNumber });
}
})(" + (data == null ? "null" : Newtonsoft.Json.JsonConvert.SerializeObject(data)) + @");
"; string result;
jsContext.EvaluateScript(js, out result);
jsContext.Dispose();
gecko.Dispose(); if(result.StartsWith("{\"______err\":"))
{
var errObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(result);
string errMsg = errObj.Value<string>("______err");
//int lineNumber = errObj.Value<int>("line") - 3;
throw new Exception(errMsg);
}
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(result);
} /// <summary>
/// 读取js文件内容
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
string readJsFile(string filePath)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filePath))
{
var data = new byte[fs.Length];
var isUtf8 = IsUTF8(fs);
fs.Position = ;
fs.Read(data, , data.Length);
if (isUtf8)
{
return Encoding.UTF8.GetString(data);
}
else
{
return Encoding.GetEncoding("gb2312").GetString(data);
}
}
} /// <summary>
/// 判断流是否是utf-8编码
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
static bool IsUTF8(Stream stream)
{
bool IsUTF8 = true; while (stream.Position < stream.Length)
{
byte b = (byte)stream.ReadByte();
if (b < 0x80) // (10000000): 值小于0x80的为ASCII字符
{ }
else if (b < (0xC0)) // (11000000): 值介于0x80与0xC0之间的为无效UTF-8字符
{
IsUTF8 = false;
break;
}
else if (b < (0xE0)) // (11100000): 此范围内为2字节UTF-8字符
{
if (stream.Position >= stream.Length - )
{
break;
}
byte nextByte = (byte)stream.ReadByte();
if ((nextByte & (0xC0)) != 0x80)
{
IsUTF8 = false;
break;
}
}
else if (b < (0xF0)) // (11110000): 此范围内为3字节UTF-8字符
{
if (stream.Position >= stream.Length - )
{
break;
} byte nextByte1 = (byte)stream.ReadByte();
byte nextByte2 = (byte)stream.ReadByte();
if ((nextByte1 & (0xC0)) != 0x80 || (nextByte2 & (0xC0)) != 0x80)
{
IsUTF8 = false;
break;
}
}
else
{
IsUTF8 = false;
break;
}
} return IsUTF8; } public void Dispose()
{
JsFiles.Clear();
}
} class JsFileReference
{
/// <summary>
/// js文件
/// </summary>
public string JsFile;
/// <summary>
/// 所依赖的js文件
/// </summary>
public IEnumerable<string> References; } }

编写单元测试基类

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Gecko; namespace Way.UnitTest.Javascript
{
/// <summary>
/// 其他js单元测试,建议继承此类
/// </summary>
[TestClass]
public class JSUnitTest
{ static JSUnitTest()
{
Xpcom.Initialize("Firefox"); //组合文件夹路径
var solutionPath = AppDomain.CurrentDomain.BaseDirectory + "\\..\\..\\..\\"; //添加全局使用的js文件
JsRunner.AddGlobalJsFile($"{solutionPath}\\js\\xpos-10.core.js"); //定义js文件的依赖关系,这里只是举例,文件实际不存在
//设置kkk.js依赖于a1.js a2.js 两个文件,
//这样,每当使用kkk.js文件,系统会自动引入a1.js a2.js 两个文件
JsRunner.SetJsReference($"{solutionPath}\\kkk.js", new string[] {
//这里写上所依赖js文件的路径
$"{solutionPath}\\a1.js",
$"{solutionPath}\\a2.js"
}); } }
}

编写测试代码

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Way.UnitTest.Javascript
{
[TestClass]
public class Example : JSUnitTest
{ [TestMethod]
public void Test()
{
using (JsRunner jsEngine = new JsRunner())
{
//编写js代码
var js = @"
return data.name;
";
//运行js代码
var result = jsEngine.Run<string>(js, new {name="JACK"});
if (result != "JACK")
throw new Exception("运算结果错误");
}
} }
}

用C#在Visual Studio写Javascript单元测试(Firefox内核)的更多相关文章

  1. 用C#在Visual Studio写Javascript单元测试

    1.在vs创建一个标准的单元测试工程 2.引用nuget包:Edge.js 我是用的是6.11.2版本 3.编写JsRunner类 using EdgeJs; using System; using ...

  2. Visual Studio 中的单元测试 UNIT TEST

    原文:Visual Studio 中的单元测试 UNIT TEST 注:本文系作者原创,可随意转载,但请注明出处.如实在不愿注明可留空,强烈反对更改原创出处.TDD(Test-Driven Devel ...

  3. 基于Visual Studio .NET2015的单元测试

    基于Visual Studio .NET2015的单元测试 1.    在Visual Studio .NET2015中创建任意项目. 2.    在某个公共类的公共方法的名称上面点击右键,选择“创建 ...

  4. 基于Visual Studio .NET2015的单元测试 OpenCover

    https://www.cnblogs.com/XiaoRuLiang/p/10095723.html 基于Visual Studio .NET2015的单元测试 1.    在Visual Stud ...

  5. Visual Studio中UnitTesting单元测试模板代码生成

             在软件研发过程中,单元测试的重要性直接影响软件质量.经验表明一个尽责的单元测试方法将会在软件开发的某个阶段发现很多的Bug,并且修改它们的成本也很低.在软件开发的后期阶段,Bug的发 ...

  6. Visual Studio 2013进行单元测试

    使用Visual Studio 2013进行单元测试--初级篇   1.打开VS2013 --> 新建一个项目.这里我们默认创建一个控制台项目.取名为UnitTestDemo 2.在解决方案里面 ...

  7. 使用Visual Studio 2013进行单元测试

    使用Visual Studio 2013进行单元测试 1.打开VS2013 --> 新建一个项目.这里我们默认创建一个控制台项目.取名为UnitTestDemo 2.在解决方案里面新增一个单元测 ...

  8. Visual Studio(VS)C++单元测试

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Visual Studio(VS)C++单元测试     本文地址:http://techie ...

  9. 1,[VS入门教程] 使用Visual Studio写c语言 入门与技巧精品文~~~~下载安装篇

    Microsoft Visual Studio是微软(俗称巨硬)公司出品的强大IDE(Integrated Development Environment 集成开发环境),功能强大齐全,界面舒服之类的 ...

随机推荐

  1. Linux后台执行

    在Linux中有时你须要将脚本(test.sh)和可运行程序(exe)后台运行,请使用例如以下方式: nohup ./test.sh & nohup ./exe & 这样执行的程序能够 ...

  2. HDU 4849 Wow! Such City!陕西邀请赛C(最短路)

    HDU 4849 Wow! Such City! 题目链接 题意:依照题目中的公式构造出临接矩阵后.求出1到2 - n最短路%M的最小值 思路:就依据题目中方法构造矩阵,然后写一个dijkstra,利 ...

  3. Python3基础(十二) 学习总结·附PDF

    Python是一门强大的解释型.面向对象的高级程序设计语言,它优雅.简单.可移植.易扩展,可用于桌面应用.系统编程.数据库编程.网络编程.web开发.图像处理.人工智能.数学应用.文本处理等等. 在学 ...

  4. C++学习之动态数组类的封装

    动态数组(Dynamic Array)是指动态分配的.可以根据需求动态增长占用内存的数组.为了实现一个动态数组类的封装,我们需要考虑几个问题:new/delete的使用.内存分配策略.类的四大函数(构 ...

  5. es6 Object.assign ECMAScript 6 笔记(六) ECMAScript 6 笔记(一) react入门——慕课网笔记 jquery中动态新增的元素节点无法触发事件解决办法 响应式图像 弹窗细节 微信浏览器——返回操作 Float 的那些事 Flex布局 HTML5 data-* 自定义属性 参数传递的四种形式

    es6 Object.assign   目录 一.基本用法 二.用途 1. 为对象添加属性 2. 为对象添加方法 3. 克隆对象 4. 合并多个对象 5. 为属性指定默认值 三.浏览器支持 ES6 O ...

  6. Android利用Volley异步载入数据完整具体演示样例(二)

    MainActivity例如以下: package cc.y; import android.app.Activity; import android.content.Context; import ...

  7. 可编程数据平面将OpenFlow扩展至电信级应用(一)

    可编程数据平面将OpenFlow扩展至电信级应用(一) 案例:基于WinPath网络处理器的电信极OpenFlow (CG-OF)client实现 作者:Liviu Pinchas, Tao Lang ...

  8. Tomcat PK Resin

    特征 Tomcat Resin 所属公司 Apache CAUCHO 用户数 多 少 可參考文档 多 少 与Eclipse集成复杂度 适中 较复杂. Eclipse下调试开发 简便 复杂.更新类后会自 ...

  9. LeetCode 961. N-Repeated Element in Size 2N Array (重复 N 次的元素)

    题目标签:HashMap 题目给了我们一个size 为 2N 的int array,其中有 N + 1 个唯一的 数字,让我们找出那个重复的数字. 利用hashset,把每一个数字存入,一旦发现有重复 ...

  10. P1656 炸铁路

    P1656 炸铁路 81通过 286提交 题目提供者kkksc03 标签图论搜索/枚举洛谷原创 难度普及+/提高 提交该题 讨论 题解 记录 最新讨论 暂时没有讨论 题目描述 因为某国被某红色政权残酷 ...