protractor-jasmine2-html-reporter 在 conf 文件中使用 'shardTestFiles': true 共享测试时不会合并所有测试的结果

protractor-jasmine2-html-reporter doesn't consolidate results for all test when tests are shared using 'shardTestFiles': true in conf file

最近我们将 e2e-tests 配置为在 Jenkins 上进行,很快我们意识到我们必须使用共享测试文件:作为完整套件的 true 选项 运行 需要很长时间才能查看 9-每天10小时。但是当我们在 conf 文件中配置以下两个选项时。测试 运行 正常,但最终报告仅显示保存路径中的最后规格 运行 结果。合并所有选项未提供完整报告。

请查找我们的 conf 文件详细信息。任何帮助将不胜感激。

根据 Aditya 提供的解决方案编辑 conf 文件。请帮忙

   var Jasmine2HtmlReporter = require('protractor-jasmine2-html-reporter');
var log4js = require('log4js');
var params = process.argv;
var args = process.argv.slice(3);

exports.config = {
  //seleniumServerJar: './node_modules/gulp-protractor/node_modules/protractor/selenium/selenium-server-standalone-2.48.2.jar',
  seleniumAddress: 'http://localhost:4444/wd/hub',
  allScriptsTimeout: 100000,
  framework: 'jasmine2',

  onPrepare: function () {

    return new Promise(function(fulfill, reject) {
      browser.getCapabilities().then(function(value) {
        reportName = value.get(Math.random(8,2)) + '_' + value.get('browserName') + '_' + Math.floor(Math.random() * 1E16);
        jasmine.getEnv().addReporter(
          new Jasmine2HtmlReporter({
            //cleanDestination: false,
            savePath: __dirname+'/target',
            //docTitle: 'Web UI Test Report',
            screenshotsFolder: 'image',
            //takeScreenshots: true,
            takeScreenshotsOnlyOnFailures: true,
            consolidate: true,
            consolidateAll: true,
             preserveDirectory: true,
            //fixedScreenshotName: true,
            filePrefix: reportName + ".html"
          })
        );
        fulfill();
      });
    });

    // browser.manage().timeouts().implicitlyWait(11000);
    var width = 768;
    var height = 1366;
    browser.driver.manage().window().setSize(768, 1366);
    browser.ignoreSynchronization = false; 
  },

  afterLaunch: function afterLaunch() {
    var fs = require('fs');
    var output = '';
    fs.readdirSync('target/').forEach(function (file) {
      if (!(fs.lstatSync('target/' + file).isDirectory()))
        output = output + fs.readFileSync('target/' + file);
    });
    fs.writeFileSync('target/ConsolidatedReport.html', output, 'utf8');

  },

  suites:{

    example:['./test/e2e/specs/**/*Spec.js',]
  },


  /*  capabilities: {
      'browserName': 'chrome'
    },*/

  multiCapabilities: [
    {
      'browserName': 'chrome'
    },
    {
      'browserName': 'firefox'
    }
  ],


  resultJsonOutputFile:'./results.json',

  // Options to be passed to Jasmine-node.
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 100000
  }
};

我目前正在努力解决同样的问题,但是在我的原型设置中它可以正常工作,而且我使用的是 BASE 配置,仅此而已。

var Jasmine2HtmlReporter = require('protractor-jasmine2-html-reporter');


exports.config = {
framework: 'jasmine',
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['**-spec**.js'],

capabilities: {
    browserName: 'chrome',                                                      
    shardTestFiles: true,                                                        
    maxInstances: 2                                                             
},

onPrepare: function() {
    jasmine.getEnv().addReporter(
      new Jasmine2HtmlReporter({
        savePath: 'target/',
        screenshotsFolder: 'images',
        consolidate: true,
        consolidateAll: false  // false in my saved config - true in tut.
    })
  );
}
}

你能看看你是否可以让分片报告只用最低限度的工作吗?

编辑:还要注意目标文件夹中发生的情况,Jasmine 可能会在您不想要的时候 overwriting/cleaning。

编辑 2:如果您选择我下面的解决方案,请确保您启动了足够的浏览器,因为您有规格 - 如果您拆分规格,我下面的解决方案会根据浏览器 ID 生成报告:

capabilities: {
    browserName: 'chrome',                                                      
    shardTestFiles: true,                                                       
    maxInstances: **2**                                                             
},

它会仍然覆盖你的记者HTML如果你想要超过2个。它会根据browser/session创建一个HTML文件用于测试它的 ID - 如果您使用 2 个浏览器实例,您将获得 2 个 HTML 个文件,不多也不少。

编辑 3:快速解决方法是不让 jasmine 清理...

cleanDestination: false,

但这似乎没有任何作用,所以在搜索和搜索之后 - 我认为 jasmine html 记者不会让我们整合比我们拥有的碎片更多的规范。 github 上的问题跟踪器没有显示任何进展。

所以我能想到的唯一解决方案是使用我下面的解决方案,使用足够的分片来支持您的大量规格,然后在完成后将其解析回一个文件。

编辑 4:您总是可以滥用 Jenkins 在实际测试运行之间连接 HTML 文件。

限制在于 'Jasmine2HtmlReporter',因为它会在并行测试 运行 时覆盖 html 报告文件。但是避免这种情况绝对是可能的,并且有几种方法可以做到这一点。根据您的方便选择正确的方式

1) 修改 Jasmine2HtmlReporter 的 'index.js' 以附加文件而不是 PhantomJs 使用

覆盖它

2) 通过从 onPrepare() 函数配置 Jasmine2HTML 报告器生成独特的 HTML 报告,并稍后合并所有报告

解决方案 1:Jasmine2HtmlReporter 的当前代码库 - index.js 使用两个函数 - phantomWrite()nodeWrite() 来写入数据。参考 here

我创建了一个新函数 - appendwrite() 用于追加而不是覆盖,并且修改了代码以获取此函数 查看我从 protractor-jasmine2-html-reporter

中分叉出来的 github 代码
        function appendwrite(path, filename, text){
            var fs = require("fs");
            var nodejs_path = require("path");
            require("mkdirp").sync(path); // make sure the path exists
            var filepath = nodejs_path.join(path, filename);
            fs.appendFileSync(filepath,text)
            return;
        }

并修改'node_modules/protractor-jasmine2-html-reporter/index.js'中的self.writeFile函数以获取新函数

        try {
            appendwrite(path, filename, text);
            //phantomWrite(path, filename, text);
            return;
        } catch (e) { errors.push('  PhantomJs attempt: ' + e.message); }
        try {
            nodeWrite(path, filename, text);
            return;
        } catch (f) { errors.push('  NodeJS attempt: ' + f.message); }

并注释以下清理新 运行 报告的代码,这样您就不会看到任何错误清理错误 - CleanUpCode

    rmdir(self.savePath);

解决方案 2:通过在 OnPrepare 函数中配置 Jasmine 报告器,为并行实例生成基于 sessionID 的单独报告

onPrepare: function() {
        return new Promise(function (fulfill, reject) {
            browser.getCapabilities().then(function (value) {
                reportName = value.get('webdriver.remote.sessionid') + '_' + value.get('browserName') + '_' + Math.floor(Math.random()*1E16);
                jasmine.getEnv().addReporter(
                    new Jasmine2HtmlReporter({
                        savePath: 'target/',
                        screenshotsFolder: 'images',
                        consolidate: true,
                        consolidateAll: true,
                        filePrefix: reportName + ".html"
                    })
                );
                fulfill();
            })
        });
    },

第 2 步:在完成完整测试并关闭所有 webdriver 会话后,在 afterLaunch() 方法中合并跨并行实例生成的报告

afterLaunch: function afterLaunch() {
        var fs = require('fs');
        var output = '';
       fs.readdirSync('target/').forEach(function(file){
           if(!(fs.lstatSync('target/' + file).isDirectory()))
            output = output + fs.readFileSync('target/' + file);
       });
        fs.writeFileSync('target/ConsolidatedReport.html', output, 'utf8');
    },

您还会看到使用一个 ConsolidatedReport 生成的报告如下所示 PS: 请忽略任何拼写错误和语法错误。这只是作为一个例子,可以定制

EDIT1:我们用来命名 HTML 报告的 'sessionID' 是 webdriver 远程 sessionID,如果您怀疑它可能无法通过多个会话保持唯一性,只需生成一个随机数用于个人 HTML 报告并稍后合并

我修改了上面的代码

Aditya 的解决方案对我来说效果很好。 我的示例配置文件: var Jasmine2HtmlReporter = require('protractor-jasmine2-html-reporter');

exports.config = { 框架:'jasmine2', seleniumAddress: 'http://localhost:4444/wd/hub', /多重能力:[ { 'browserName': 'chrome', 'shardTestFiles':是的, 'maxInstances': 2, 铬选项:{ args: ['chrome.switches', '--disable-extensions'] } }, { 'browserName': 'firefox' } ],/ 能力: { 'browserName': 'chrome', 'shardTestFiles':是的, 'maxInstances': 2, 铬选项:{ args: ['chrome.switches', '--disable-extensions'] } }, 套房:{ 登录页面:'login.js', 添加产品:'addproduct.js' }, //规格:['addproduct.js'], 茉莉花节点选项:{ onComplete:空, isVerbose: false, includeStackTrace:真, 显示颜色:真实, 默认超时间隔:30000 },

onPrepare: function() {
    return new Promise(function(fulfill, reject) {
        browser.getCapabilities().then(function(value) {
            reportName = value.get(Math.random(8,2)) + '_' + value.get('browserName') + '_' + Math.floor(Math.random() * 1E16);
            jasmine.getEnv().addReporter(
                new Jasmine2HtmlReporter({
                    //cleanDestination: false,
                    savePath: 'target/',
                    //docTitle: 'Web UI Test Report',
                    screenshotsFolder: 'image',
                    //takeScreenshots: true,
                    //takeScreenshotsOnlyOnFailures: true,
                    consolidate: true,
                    consolidateAll: true,
                   // preserveDirectory: true,
                    //fixedScreenshotName: true,
                    filePrefix: reportName + ".html"
                })
            );
            fulfill();
        });
    });
},
afterLaunch: function afterLaunch() {
    var fs = require('fs');
    var output = '';
    fs.readdirSync('target/').forEach(function(file) {
        if (!(fs.lstatSync('target/' + file).isDirectory()))
            output = output + fs.readFileSync('target/' + file);
    });
    fs.writeFileSync('target/ConsolidatedReport.html', output, 'utf8');
}

}

我使用以下解决方案创建了具有唯一时间戳的唯一文件夹。这会将 HTML 报告保存在日期时间戳文件夹中,而不是 Jasmin-2-html-reporter 将其删除。

var today = new Date();
   var timeStamp = today.getMonth() + 1 + '-' + today.getDate() + '-' + today.getFullYear() + '-' + 

today.getHours() + 'h-' + today.getMinutes() + 'm-' +today.getSeconds()+'s';

 jasmine.getEnv().addReporter(
            new Jasmine2HtmlReporter({
                savePath: './Reports/testResultsReport '+timeStamp,
                screenshotsFolder: 'screenPrints',
                takeScreenshots: true,
                takeScreenshotsOnlyOnFailures: true,
            })
        ); 

`

这是另一个解决方案,建立在 之上,使用 protractor-html-reporter-2,它适用于 jasmine-reporters 生成的 xml 文件。但是 jasmine-reporters 没有任何选项可以处理由多个浏览器实例生成的报告。在找不到理想的解决方案后,我最终在量角器配置 js 文件中执行了以下操作:

// add relevant packages in package.json
'use strict';
const HTMLReport = require('protractor-html-reporter-2');
const jasmineReporters = require('jasmine-reporters');
const moment = require('moment');
const os = require('os');
const xmldoc = require('xmldoc');
...
const DATE_FORMAT = 'YYYYMMDD-HHmmss-SSS'; // use any other format that gives unique timestamp
const reportDir = path.join(__dirname, '../report');
...
exports.config = {
    ...
    framework: 'jasmine',
    capabilities: {
        browserName: 'chrome',
        maxInstances: 2,
        shardTestFiles: true,
    },
    beforeLaunch: async function () {
        // clean up report directory
        fs.emptyDirSync(reportDir);
    }, 
    onPrepare: async function () {
         const NOW = moment().format(DATE_FORMAT);
         const reportName = 'index-' + NOW;
         jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter({
             consolidateAll: true,
             savePath: reportDir,
             filePrefix: reportName,
         }));

        // for screenshots
        jasmine.getEnv().addReporter({
    specDone: function (result) {
        if (result.status === 'failed') {
            browser.getCapabilities().then(function (caps) {
                const browserName = caps.get('browserName');
                browser.takeScreenshot().then(function (png) {
                    const stream = fs.createWriteStream(reportDir + '/screenshots/' + browserName + '-' + result.fullName.replace(/[.:]/g, ' ') + '.png');
                    stream.write(Buffer.from(png, 'base64'));
                    stream.end();
                });
            });
        }
    }
});
    },
    onComplete: async function () {
        // do something after each instance of browser is closed
    },
    afterLaunch: async function (exitCode) {
        // do something after ALL instances of browser are closed
        await consolidateJasmineXmlReports();
    },
    ...     
},
...
async function consolidateJasmineXmlReports() {
    // there may be better ways to write xml out but this works for me
    const files = fs.readdirSync(reportDir).filter(fn => fn.endsWith('.xml'));
    let disabledSum = 0;
    let errorsSum = 0;
    let failuresSum = 0;
    let testsSum = 0;
    let timeSum = 0;
    const allTestSuiteNodes = [];
    for (const file of files) {
        const pathToXml = reportDir + path.sep + file;
        console.log('Reading xml report file: ' + pathToXml);
        const xml = fs.readFileSync(pathToXml);
        const xmlDoc = new xmldoc.XmlDocument(xml);
        const disabled = parseInt(xmlDoc.attr.disabled);
        const errors = parseInt(xmlDoc.attr.errors);
        const failures = parseInt(xmlDoc.attr.failures);
        const tests = parseInt(xmlDoc.attr.tests);
        const time = parseFloat(xmlDoc.attr.time);
        disabledSum += disabled;
        errorsSum += errors;
        failuresSum += failures;
        testsSum += tests;
        timeSum += time;

        const testSuiteNodes = xmlDoc.childrenNamed('testsuite');
        allTestSuiteNodes.push(testSuiteNodes);
    }

    let startXml = `<?xml version="1.0" encoding="UTF-8" ?>`;
    startXml += `<testsuites disabled="` + disabledSum + `" errors="` + errorsSum + `" failures="` + failuresSum + `" tests="` + testsSum + `" time="` + timeSum + `">`;
    const endXml = '</testsuites>';
    allTestSuiteNodes.push(endXml);
    const finalXml = startXml + allTestSuiteNodes.join('\n');
    fs.writeFileSync(reportDir + path.sep + 'consolidated.xml', finalXml, 'utf8');
        
    const testConfig = {            
        outputPath: reportDir,
        outputFilename: 'consolidated',
        screenshotPath: './screenshots',
        screenshotsOnlyOnFailure: true,
        ...
    };

    new HTMLReport().from(reportDir + path.sep + 'consolidated.xml', testConfig);
}

逻辑是

  1. 确保所有 xml 个文件的唯一名称。
  2. 将所有 xml 文件合并为 afterLaunch.
  3. 中的一个有效 xml
  4. 使用任何使用该 xml 文件的包在 afterLaunch.
  5. 中生成 html

我们使用 Jenkins 运行 上面创建的测试和报告在 Jenkins 中显示良好,并且在 Open Blue Ocean Jenkins 插件显示的报告中也准确显示。

注意:我已经使用 shardTestFiles 进行了测试,但没有使用 multiCapabilities 进行测试,但我认为它也应该适用。

另请参阅: