Create a custom reporter

Use SRF's public API to create your own custom reporter. Use your custom reporter when running Selenium tests remotely, instead of calling SRF's native reporter.

Disable the SRF reporter

Before you create your own custom reporter, disable SRF's native reporter in your test.

To do this, add the reportingEnabled capability to your script, and set the value to false.

For more details, see Reporting capability reference.

Back to top

Create your custom reporter

Create and configure your custom reporter by updating your Selenium script.

Do the following:

  1. Send a POST REST request to the SRF_REPORTER_URL, including the /api/v1/reporter/reports URL, and details as shown in the example below.

    The POST response will contain the reportID definition, shown as obj.session in the example below. Add the SRF_REPORT_ID capability to your script, with this reportID as its value.

    Example:

    var clients = require('restify-clients');
    var Q = require("q");
    client = clients.createJsonClient({ url: <SRF URL placeholder> });
    Q.nfcall(client["post"].bind(client), "/api/v1/reporter/reports", { clientId: <SRF client ID placeholder>, clientSecret: <SRF client secret placeholder> })
    .spread((req, res, obj) => {
       capabilities.SRF_REPORT_ID = obj.session;
    });
  2. Define steps for each of your test steps.

    For each step, send a POST REST request to the SRF URL, with the /api/v1/reporter/reports/:reportId/steps URL, and the step details in the body, as described in the table below.

    For example, see Step details example.

    Note: The reported steps will be displayed in the script run results only once a Selenium session is open in this script (after you build a web driver).

    Step details included in the body of the POST REST request

    In the following table, click the links to jump to the sample code.

    description

    Required. A string value that describes the step in human-readable language.

    This text appears in the report.

    role

    Required. Do one of the following:

    Step includes no children:

    If the step includes no children, define the role as one of the following:

    • regular
    • checkpoint
    • verification

    Step is a parent

    If the step is a parent of child steps, name your parent step, and append -begin to the end of its name.

    After the last child step, be sure to define the end of the parent, by appending -end to the parent name.

    index Required. Add a index number for each step, to enable SRF to sort and display the step results in the correct order.
    status

    Optional. Add the step's run status, one of the following:

    • failed
    • success
    • errored
    • comment
    errors Optional. Add an array of error messages that may occur during the step run, so that these messages can be displayed in the step run results.
    durationMs Optional. Add the step duration, in milliseconds (ms).
    snapshot

    Optional. Configure snapshot captures in your test.

    Do the following:

    1. Use the webdriver takeScreenshot method, and convert the result to base64.

    2. Add the result to the snapshot code.

    Step details example

    Q.nfcall(client["post"].bind(client), "/api/v1/reporter/reports/" + reportID + "/steps", {

    description: "Parent step",

    role: "parent-step-begin",

    index: 1

    }).then(() => {

    ...

    return Q.nfcall(client["post"].bind(client), "/api/v1/reporter/reports/" + reportID + "/steps", {

    description: "First child step (with no snapshot)",

    role: "regular",

    index: 2,

    status: "failed",

    errors: ["Failed to get element..."],

    durationMs: 3.45

    });

    }).then(() => {

    ...

    ...

    return driver.takeScreenshot();

    }).then((image, err) => {

    let snapshot = image.toString('base64');

    return Q.nfcall(client["post"].bind(client), "/api/v1/reporter/reports/" + reportID + "/steps", {

    description: "Second child step (with snapshot)",

    role: "regular",

    index: 3,

    status: "success",

    durationMs: 1.23,

    snapshot: snapshot

    });

    }).then(() => {

    Q.nfcall(client["post"].bind(client), "/api/v1/reporter/reports/" + reportID + "/steps", {

    description: "Parent step",

    role: "parent-step-end",

    index: 4

    })

    });

  3. Add the endReport API to close the session.

    For example:

    Q.nfcall(client["del"].bind(client), "/api/v1/reporter/reports/" + reportID);

For a full code sample, see JavaScript reporter sample with framework

For more details, see the Reports section of the SRF interactive API help.

Back to top

JavaScript reporter sample with framework

The following code sample runs a test on Chrome and Ubuntu in a custom framework, using a custom-built reporter.

var webdriver = require('selenium-webdriver'),
By = webdriver.By;
var HttpsProxyAgent = require('https-proxy-agent');
// integ demo
var srfClientId = "<PLACEHOLDER>";
var srfClientSecret ="<PLACEHOLDER>";
var srfServerUrl = "<PLACEHOLDER>";
var testName;
var MyFramework;
var capabilities = {
browserName: 'chrome',
version: 'latest',
testName: "Custom Reporter Api",
resolution: "1024x768",
platform: "Windows 10",
SRF_CLIENT_ID: srfClientId,
SRF_CLIENT_SECRET: srfClientSecret
};
before((done) => {
MyFramework.start(done);
});
describe('SRF Test', function () {
it('test case ', function (done) {
MyFramework.startTest('test case 1', capabilities, done);
MyFramework.seleniumStep("get", "http://example.com/");;
MyFramework.seleniumStepWithSnapshot("findElement", webdriver.By.partialLinkText("More"));
MyFramework.endTest(done);
});
});
after((done) => {
MyFramework.end(done);
});
(function () {
var clients = require('restify-clients');
var Q = require("q");
var promiseChain;
var driver;
 
MyFramework = {
start:(done) => {
console.log("###~> Starting suite");
MyFramework.lastStatus = "success";
promiseChain = ReporterApi.startReport().then((reportId) => {
return ReporterApi.addStep({ description: "Meaningful description", role: "call-me-as-you-wish-begin" });
}).then(() => {
console.log("Started");
done && done();
});
},
end:(done) => {
promiseChain = promiseChain.then(() => {
return ReporterApi.addStep({ description: "Meaningful description", role: "call-me-as-you-wish-end", status: MyFramework.lastStatus });
}).then(() => {
console.log("<~### Suite ended");
return ReporterApi.endReport().then(() => done && done());
}).catch(err => { console.log ("Error:", err); });
},
startTest: (title, capabilities, done) => {
console.log("###~> Starting test case", title);
capabilities.automaticReport = "off";
MyFramework.lastStatus = "success";
promiseChain = promiseChain.then(() => {
return ReporterApi.addStep({ description: title, role: "test-case-begin" });
}).then(() => {
capabilities.SRF_REPORT_ID = ReporterApi.reportId;
driver = new webdriver.Builder().withCapabilities(capabilities)
.usingServer(srfServerUrl + "/wd/hub")
.usingHttpAgent(new HttpsProxyAgent(process.env.http_proxy))
.build();
 
webdriver.promise.controlFlow().on('uncaughtException', function handleError(e) {
console.error('ERROR: ' + e);
driver.quit().finally(done);
webdriver.promise.controlFlow().removeListener('uncaughtException', handleError);
});
console.log("Test case started");
});
},
endTest: (done) => {
promiseChain = promiseChain
.then(() => {
return driver && driver.quit();
}).then(() => {
return ReporterApi.addStep({ description: "ggg", role: "test-case-end", status: MyFramework.lastStatus });
}).then(() => done && done())
.catch(err => { console.log ("Error:", err); });
},
 
seleniumStep: (method, ...arguments) => {
promiseChain = promiseChain.then(() => {
console.log("---> Selenium step: driver.%s (%s); with no snapshot", method, arguments.join());
return MyFramework._seleniumStep(method, arguments);
});
return promiseChain;
},
 
seleniumStepWithSnapshot: (method, ...arguments) => {
promiseChain = promiseChain.then(() => {
console.log("---> Selenium step: driver.%s (%s); taking snapshot", method, arguments.join());
return driver.takeScreenshot();
}).then((image, err) => {
return MyFramework._seleniumStep(method, arguments, image.toString('base64'));
});
return promiseChain;
},
 
_seleniumStep: (method, argsArray, snapshot) => {
var error;
var startTime;
return Q.when().then(() => { startTime = new Date().getTime(); })
.then(() => {
return driver[method].apply(driver, argsArray);
}).catch((err) => {
error = err;
MyFramework.lastStatus = "failed";
}).then(() => {
var duration = new Date().getTime() - startTime;
return ReporterApi.addStep({
description: "Selenium step: " + method + "(" + argsArray.join() + " )",
status: error ? "failed" : "success",
role: "regular",
snapshot: snapshot,
errors: error ? [ error ] : undefined,
durationMs: duration
});
}).then(() => { console.log("<--- Selenium step ended"); });
}
};
 
var client;
var ReporterApi = {
startReport: () => {
console.log("Starting report on", srfServerUrl);
client = clients.createJsonClient({ url: srfServerUrl });
ReporterApi.currStepIndex = 1;
return ReporterApi._sendRequest("post", "/api/v1/reporter/reports", { clientId: srfClientId, clientSecret: srfClientSecret })
.then((result) => {
console.log("Report started");
ReporterApi.reportId = result.session;
return result.session;
}).catch(err => {console.log("start error:", err); });
},
addStep: (step) => {
console.log("Adding step");
step.index = ReporterApi.currStepIndex++;
return ReporterApi._sendRequest("post", "/api/v1/reporter/reports/" + ReporterApi.reportId + "/steps", step).catch(err => {console.log("add step error:", err); });
},
 
endReport: () => {
console.log("Ending report");
return ReporterApi._sendRequest("del", "/api/v1/reporter/reports/" + ReporterApi.reportId).catch(err => {console.log("end error:", err); });
},
 
_sendRequest: (method, url, body) => {
console.log("Request:", method, url);
return Q.when().then(() => {
if (body)
return Q.nfcall(client[method].bind(client), url, body);
return Q.nfcall(client[method].bind(client), url);
}).catch((err) => { console.log("Error:", err); })
.spread((req, res, obj) => {
console.log('Response: %d -> %j', res && res.statusCode, obj);
return obj;
});
}
};
})();

Back to top

See also: