用C#在Visual Studio写Javascript单元测试(Firefox内核)

引用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(10);
                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 = 0;
                fs.Read(data, 0, 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 - 1)
                    {
                        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 - 2)
                    {
                        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("运算结果错误");
            }
        }

    }
}

 

posted @ 2018-03-14 09:58  IWing  阅读(351)  评论(0编辑  收藏  举报