Read Me Page Accessibility, HTML, CSS, Broken Links, Spell Checking

Web API

This document explains how to use the API with Total Validator CI so that you can run the full range of Total Validator tests (accessibility, HTML, spelling, etc.) on your web pages as part of your normal testing.

Overview

Start by running Total Validator CI using the batch/script files provided together with the -startlistener option, so that it runs in the background waiting for API requests to test something.

Configure your application to send a HTTP request with the necessary information to perform the test. The CI application will then run the test and return to your application the same status code as the -extendedstatus option (you don't need to specify this option).

Note: Total Validator CI only responds to requests from the same computer, so your application must run on the same system as Total Validator CI.

Running Total Validator CI

Please see the CI documentation for how to configure and run the CI version. As a minimum you need to use the -startlistener and -id options. Typically you will want to configure other options (such as -accessibility and -suppressresults) which are common to all the pages you wish to test. If there are a lot of options you should consider putting them into a CI properties file for convenience.

You can either run the CI batch/script file from the command line (or PowerShell for Windows), or even start it directly from your application. But please note that you must run the batch/script file from the folder where it resides. so that Total Validator can find all the resources needed.

If you are running the CI batch/script file from the command line, then when you've finished testing, you will have to type Ctrl-C or Command-C to stop it, or just close the window it is running in. Alternatively, you can call the API to tell it to stop (see below).

Calling the API using HTTP/1 GET

The simplest way to call the API is to make a HTTP/1 GET request to the CI application passing the URL of the page you wish to validate. In simple terms you could type this into your browser:

http://localhost:9889/api/v1/?-url=https://www.mysite.com

Of course, in practice you wouldn't use your browser to send the request to the API, but instead code this request in your application.

Only requests to the local computer will work (localhost or 127.0.0.1) and 9889 specifies the port the CI application is listening on. You can use the -listenport option when starting the CI application to change the default from 9889. The path /api/v1/ tells the CI application it is an API request, and you must add the -url or -file command line option as a query parameter, so that it has something to test.

When the test is complete the CI application will return a HTTP 200 status code together with the -extendedstatus value.

When you've finished all of your testing, you can stop the CI application by calling the API with this URL: http://localhost:9889/api/v1/stop/

Specifying options

Testing will be performed using whatever options you specified when starting the CI application. But you can add other command line options to the request to supplement or to override those used when starting the CI application. For example:

http://localhost:9889/api/v1/?-url=https%3A%2F%2Fwww.mysite.com&-suppressresults=&-dtd=Auto-detect

Note that you should always encode all values (hence the use of https%3A%2F%2Fwww.mysite.com). Also note the use of -suppressresults= for parameters with no values. You can also use the -properties option to reference an entire file of options to use (this file must be available to the CI application to read).

Duplicate options are dealt with in a similar way as with the command line. Options are processed in the order they appear in the GET request, and override any supplied when starting the CI application. You can also make use of the :remove suffix to ignore boolean options.

For reference the priority is as follows: Options in query parameters override any option used to start the CI application. In any group, options in a -properties referenced file are treated as the lowest priority.

Java example

Here is a simple example using core Java, although using third part libraries (or the new HttpClient in Java 11+) is recommended.

// The url to send the request to
String requestUrl = "http://localhost:9889/api/v1/";

// Add the mandatory -url (or -file) option and always encode values
requestUrl += "?-url=" + URLEncoder.encode("https://www.mysite.com", StandardCharsets.UTF_8);
requestUrl += "&-suppressresults=";  // Leave boolean values blank

// Send the request
URL url = new URL(requestUrl);
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
httpConnection.connect();

// Deal with the response
if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
  // Read the response (The "-extendedstatus" value)
  InputStream is = httpConnection.getInputStream();
  . . .
}

Javascript example

Here is a simple example using Javascript. Note that we use a synchronous request in the example for brevity, but this is not recommended.

// The url to send the request to
var requestUrl = "http://localhost:9889/api/v1/";

// Add the mandatory -url (or -file) option and always encode values
requestUrl += "?-url=" + encodeURIComponent("https://www.mysite.com");
requestUrl += "&-suppressresults=";  // Leave boolean values blank
  
// Send the request (synchronously for brevity)
var request = new XMLHttpRequest();
request.open("GET", requestUrl, false);
request.send(null);

// Deal with the response
if (request.status === 200) {
  console.log(request.responseText); // The "-extendedstatus" value
}

Calling the API using HTTP/1 POST

Using a GET request you can only test pages using the -url or -file options. However, a common requirement is to send the source of the page to be tested directly to the API. Sending the source is also the easiest way of using the API to test pages that require authentication to access, and is the most convenient solution when calling the API during a Selenium test. However, you can only test one page at a time this way.

For this you must use a HTTP/1 POST request with the data encoded as multipart/form-data. Requests should be sent to http://localhost:9889/api/v1/. Note that only requests to the local computer will work (localhost or 127.0.0.1) and 9889 specifies the port the CI application is listening on. You can use the -listenport option when starting the CI application to change the default from 9889. Finally, the path /api/v1/ tells the CI application it is an API request.

The page source must be attached using the name "source" and be of the correct content type (typically text/html or text/css). See the example code below, showing how to do this for Java and Javascript. It also must use the UTF-8 character set, or be a subset of this (for example ASCII or ISO-8859-1).

When the test is complete the CI application will return a HTTP 200 status code together with the -extendedstatus value.

When you've finished all of your testing, you can stop the CI application by calling the API with this URL: http://localhost:9889/api/v1/stop/

Specifying options

Testing will be performed using whatever options you specified when starting the CI application. But you can add other command line options to the request to supplement or to override those used when starting the CI application. These may be added as query parameters to the request URL as shown with HTTP/1 GET requests, but more typically you would add them as extra form data.

One of these options could be -properties to reference an entire file of options to use (this file must be available to the CI application to read). Alternatively (or in addition), you can also attach a CI properties file to the request, just like with the page source (see examples below). This must be sent using the name "properties", be of the content type text/plain and be in ASCII or UTF-8 format.

As only one page can be tested, any -pages value (and other Include options) will be ignored. However, -brokenlinks (and other Link options) may be used to check for broken links. In this case you should supply a -url or -file value to use for testing any relative links, otherwise you may see lots of broken link errors in the results.

Duplicate options are dealt with in a similar way as with the command line. Options are processed in the order they appear in the POST request, and override any supplied when starting the CI application. You can also make use of the :remove suffix to ignore boolean options.

For reference the priority is as follows: Options in query parameters override any options in form data. These in turn override any "properties" file that is sent. These in turn override any option used to start the CI application. In any group, options in a -properties referenced file are treated as the lowest priority.

Using POST for everything

Instead of using different application code for GET and POST requests, you can use POST for all API requests, to simplify your code. For the equivalent of GET requests, just don't send the "source" of a page, but you must send the -url or -file option to reference the start page to test.

Java example using Apache HttpClient

As native support for sending multipart/form-data in Java is poor, below is an example using the popular Apache HttpClient library.

Here we obtain the page source from the Selenium WebDriver using getPageSource(), but this could be populated in other ways. As the getPageSource() call doesn't include the <!DOCTYPE>, we've added this to the source. We also send the contents of a CI properties file containing additional options, although this is optional.

// Get the page source and contents of a properties file
String source = "<!DOCTYPE html>" + webDriver.getPageSource();
String properties = Files.readString(Paths.get("properties.txt"));

// Send the request and deal with the response
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
  HttpPost httpPost = new HttpPost("http://localhost:9889/api/v1");

  // Note the names "source" and "properties" must be used for these parts
  HttpEntity reqEntity = MultipartEntityBuilder.create()
      .addTextBody("-dtd", "Auto-detect")  // Add whatever options you want
      .addTextBody("-suppressresults", "") // For 'boolean' options send an empty string
  // Because ContentType.TEXT_HTML uses ISO-8859-1, we create our own without a charset
      .addTextBody("source", source, ContentType.create("text/html"))  // Use "text/css" for CSS
      .addTextBody("properties", properties, ContentType.TEXT_PLAIN)
      .build();
  httpPost.setEntity(reqEntity);

  try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
    int status = response.getCode();
    if (status == HttpURLConnection.HTTP_OK) {
      HttpEntity resEntity = response.getEntity();
      // The "-extendedstatus" value
      System.out.println("Response: " + EntityUtils.toString(resEntity));
    }
  }
}

Note: Apache HttpClient adds the ISO-8859-1 character set to all Content-Type headers. This could cause Total Validator CI to read the source using the wrong character set, and raise test errors that don't appear when using the Pro version. So we use ContentType.create("text/html") to avoid this.

Javascript example

When sending the source of a page to the API using Javascript, use FormData Objects so that multipart/form-data encoding is automatically used. Note that we use a synchronous request in the example for brevity, but this is not recommended.

// Create the FormData object
var formData = new FormData();
formData.append("-dtd", "Auto-detect")   // Add whatever options you want
formData.append("-suppressresults", ""); // For 'boolean' options send an empty string

// Attach the current page source
var page = document.documentElement.innerHTML;
var sourceBlob = new Blob([page], { type: "text/html"});  // Use "text/css" for CSS Pages
formData.append("source", sourceBlob);   // You must name this "source"

// Optionally, you can send the contents of a "properties" file with additional options
var props = '-dtd=Auto-detect\n-css=3';  // Contents of a "properties" file
formData.append("properties", props);    // You must name this "properties"

// Send the request (synchronously for brevity)
var request = new XMLHttpRequest();
request.open("POST", "http://localhost:9889/api/v1", false);
request.send(formData);

// Deal with the response
if (request.status === 200) {
  console.log(request.responseText); // The "-extendedstatus" value
}