Skip to content

Parallel Robot Framework pipeline

This directory provides an tutorial of a Jenkins pipeline that executes Robot Framework automation tasks with docker agent in parallel stages as well as combines and stores the produced HTML/XML report files.

Preparing the Jenkins instance

The pipeline provided by this tutorial can be added to any Jenkins instance you have administrator access and can run pipeline stages with docker agent. For example, Jenkins configuration from Jenkins with access to hosts Docker engine tutorial can be used.

In order to be able to run the pipeline we will need Docker Pipeline and Robot Framework plugins. Install these plugins through Available tab in Manage Jenkins > Manage Plugins and restart the Jenkins instance after these plugins have been installed. The restart can be done, for example, from the plugins page or by restarting the container with docker compose down and docker compose up.

Configure the pipeline

First, create a new pipeline via New Item button in the rigth side menu of the Jenkins dashboard. The name of the pipeline could be for example Screenshots and it should be an pipeline.

In the configure pipeline view, scroll to the bottom and under Pipeline sub-header select Pipeline script from SCM. SCM type should be Git and Repository URL the url of this repository: https://github.com/cicd-tutorials/cicd-tutorials.net.git. Ensure that branch specifier includes main branch of the repository and modify the Script Path to be docs/tutorials/jenkins/parallel-robot-pipeline/Jenkinsfile.

The pipeline executes the same Robot Framework suite twice: once with Firefox and once with Chromium. This is done in parallel. After the test suites have finished, the log files are combined in the next stage.

Jenkinsfile
String d = "docs/tutorials/jenkins/parallel-robot-pipeline"

pipeline {
    agent any
    parameters {
        string(
            name: 'URL',
            defaultValue: '',
            description: 'Target URL for the Robot Framework tasks')
    }
    stages {
        stage('Run tasks') {
            parallel {
                stage('Chromium') {
                    agent { docker {
                        image 'ghcr.io/cicd-tutorials/robot-browser:latest'
                        args '--entrypoint=""'
                        reuseNode true
                    } }
                    steps {
                        sh "robot -d robot_output -l none -r none -o chromium.xml -N Chromium -v URL:${params.URL} --nostatusrc $d/suites/"
                        // If reuseNode true was not used, we would have to stash the output XML.
                        // stash includes: 'robot_output/**', name: 'Chromium'
                    }
                }
                stage('Firefox') {
                    agent { docker {
                        image 'ghcr.io/cicd-tutorials/robot-browser:latest'
                        args '--entrypoint=""'
                        reuseNode true
                    } }
                    steps {
                        sh "robot -d robot_output -l none -r none -o b.xml -N Firefox -v URL:${params.URL} --nostatusrc $d/suites/"
                        // If reuseNode true was not used, we would have to stash the output XML.
                        // stash includes: 'robot_output/**', name: 'Firefox'
                    }
                }
            }
        }
        stage('Process logs') {
            agent { docker {
                image 'ghcr.io/cicd-tutorials/robot-browser:latest'
                args '--entrypoint=""'
                reuseNode true
            } }
            steps {
                // If reuseNode true was not used, we would have to unstash the output XMLs.
                // unstash 'Chromium'
                // unstash 'Firefox'
                sh "rebot -d rebot_output -o output.xml -N '${env.JOB_BASE_NAME} ${BUILD_DISPLAY_NAME}' --nostatusrc robot_output/*.xml"
            }
        }
    }
    post {
        success {
            robot outputPath: 'rebot_output', otherFiles: '**/*.png', onlyCritical: false, passThreshold: 100.0, unstableThreshold: 0.0
        }
    }
}

After you have created the pipeline, try to execute it by clicking Build Now. All Robot Framework tasks should be in Skipped state as we did not specify an URL variable. In addition, after the first execution Jenkins should have updated the project configuration to contain parameters defined in the pipeline and we can now pass target URL to our automation tasks in Build with Parameters menu.

The Robot Framework suite defined in suites/screenshot.robot uses Browser library to take a screenshot of the page available in the URL defined with the URL variable.

suites/screenshot.robot
*** Settings ***
Library             OperatingSystem
Library             Browser
Suite Setup         Check URL and open browser
Suite Teardown      Close browser

*** Variables ***
${BROWSER}          chromium
${URL}              ${EMPTY}

*** Tasks ***
Capture Screenshot
    Skip if  not $URL  msg=Target URL not specified
    New Page  ${URL}
    Take Screenshot  EMBED

*** Keywords ***
Open browser defined by environment
    ${browser}=  Get Environment Variable    BROWSER    ${BROWSER}
    New Browser  ${browser}
    New Context  viewport={'width': 1280, 'height': 720}

Check URL and open browser
    Skip if  not $URL  msg=Target URL not specified
    Open browser defined by environment

Finally, If the robot log cannot be loaded after task execution, see this stackoverflow post for solution. To summarize, run following command in Jenkins Script Console to modify Jenkins servers Content Security Policy (CSP):

System.setProperty("hudson.model.DirectoryBrowserSupport.CSP","sandbox allow-scripts; default-src 'none'; img-src 'self' data: ; style-src 'self' 'unsafe-inline' data: ; script-src 'self' 'unsafe-inline' 'unsafe-eval' ;")

Building and running the Docker container

The Jenkins pipeline listed above uses container image from Github Container Registry to run the pipelines. The container image is created using Dockerfile defined by this tutorial.

Dockerfile
FROM mcr.microsoft.com/playwright:v1.48.0-noble AS base

# Disable interactive configuration
ENV DEBIAN_FRONTEND='noninteractive'

WORKDIR /work
COPY ./entrypoint.sh ./requirements.txt /work/
RUN apt-get update && \
    apt-get install -y \
        python3-pip && \
    pip install --break-system-packages -r requirements.txt && \
    rfbrowser init && \
    chmod +x entrypoint.sh

ENTRYPOINT ["./entrypoint.sh"]


FROM base

COPY suites/ suites/
COPY --from=base /work/entrypoint.sh .

The Dockerfile is based on Playwright image that contains the Browser binaries and other Playwright related dependencies of the Browser library. In addition, the Dockerfile installs Python and Python libraries defined in requirements.txt to the container image.

requirements.txt
robotframework>=4
robotframework-browser>=18

Running the tasks locally

Build the Docker containers with docker build:

docker build . --tag rf-screenshot

Execute the Robot Framework suites with docker run:

# Chromium
docker run --rm -v $(pwd)/out:/out -e BROWSER=chromium rf-screenshot -d /out -v URL:https://cicd-tutorials.net/

# Firefox
docker run --rm -v $(pwd)/out:/out -e BROWSER=firefox rf-screenshot -d /out -v URL:https://cicd-tutorials.net/