{"id":6213,"date":"2020-10-20T17:50:40","date_gmt":"2020-10-20T17:50:40","guid":{"rendered":"https:\/\/berg-software.com\/?p=6213"},"modified":"2022-09-16T13:38:10","modified_gmt":"2022-09-16T13:38:10","slug":"end-to-end-tests-and-gitlab-integration","status":"publish","type":"post","link":"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/","title":{"rendered":"End-to-end tests journey &#038; Integration into a GitLab pipeline"},"content":{"rendered":"<p>[et_pb_section fb_built=&#8221;1&#8243; fullwidth=&#8221;on&#8221; _builder_version=&#8221;4.4.1&#8243;][et_pb_fullwidth_post_title meta=&#8221;off&#8221; featured_placement=&#8221;background&#8221; _builder_version=&#8221;4.7.7&#8243; title_font=&#8221;||||||||&#8221; title_text_color=&#8221;#ff6317&#8243; title_font_size=&#8221;3.5em&#8221; meta_font=&#8221;|300|||||||&#8221; meta_text_color=&#8221;#ffffff&#8221; meta_font_size=&#8221;1em&#8221; background_enable_color=&#8221;off&#8221; use_background_color_gradient=&#8221;on&#8221; background_color_gradient_start=&#8221;rgba(248,248,248,0.85)&#8221; background_color_gradient_end=&#8221;rgba(248,248,248,0.75)&#8221; background_color_gradient_overlays_image=&#8221;on&#8221; min_height=&#8221;20vh&#8221; height=&#8221;400px&#8221; custom_padding=&#8221;6vh||6vh||false|false&#8221; global_module=&#8221;403&#8243; locked=&#8221;off&#8221;][\/et_pb_fullwidth_post_title][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;intro&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2>Intro to end-to-end tests &amp; GitLab integration<\/h2>\n<p>During our microservices journey from the past years, we have faced several issues when developing, maintaining, and integrating the end-to-end tests into our CI\/CD pipeline. Let\u2019s look at the most important topics that we encountered, and -where is the case- how we solved them:<\/p>\n<ul>\n<li><a href=\"#Whatareendtoendtests\" title=\"Go to: What are end-to-end tests?\">What are end-to-end tests?<\/a><\/li>\n<li><a href=\"#Whywriteendtoendtests\" title=\"Go to: Why do we need to write end-to-end tests?\">Why do we need to write end-to-end tests?<\/a><\/li>\n<li><a href=\"#Whentowriteendtoendtests\" title=\"Go to: When to write end to end tests?\">When to write end-to-end tests?<\/a><\/li>\n<li><a href=\"#Howmuchtesttowrite\" title=\"Go to: How much end-to-end test to write?\">How much end-to-end test to write?<\/a><\/li>\n<li><a href=\"#GitLabbasics\" title=\"Go to: GitLab basics\">GitLab basics<\/a><\/li>\n<li><a href=\"#Howtosetuptherunnersandcaching\" title=\"Go to: How to: set up the runners and caching\">How to: set up the runners and caching<\/a><\/li>\n<li><a href=\"#Howtosetupthepipeline\" title=\"Go to: How to: set up the pipeline\">How to: set up the pipeline<\/a><\/li>\n<li><a href=\"#Howtoisolatethetests\" title=\"Go to: How to: isolate the tests\">How to: isolate the tests<\/a><\/li>\n<li><a href=\"#Howtopreparetheenvironmentforrunningthetests\" title=\"Go to: How to: Prepare the environment for running the tests\">How to: prepare the environment for running the tests<\/a><\/li>\n<li><a href=\"#Howtopreparethedataforthetests\" title=\"Go to: How to: prepare the data for the tests\">How to: prepare the data for the tests<\/a><\/li>\n<li><a href=\"#Howtorunthetestswithdockercompose\" title=\"Go to: How to: run the tests with Docker Compose\">How to: run the tests with Docker Compose<\/a><\/li>\n<li><a href=\"#Howtocleanupafterthetests\" title=\"Go to: How to: clean up after the tests\">How to: clean up after the tests<\/a><\/li>\n<li><a href=\"#Howtomakethetestsasfastaspossible\" title=\"Go to: How to: make the tests as fast as possible\">How to: make the tests as fast as possible<\/a><\/li>\n<li><a href=\"#Howtomaintainthetestsuite\" title=\"Go to: How to: maintain the test suite\">How to: maintain the test suite<\/a><\/li>\n<li><a href=\"#Conclusions\" title=\"Go to: Conclusions\">Conclusions<\/a><\/li>\n<\/ul>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2>Prerequisites<\/h2>\n<p>In order to make the best use of this article, at least a basic understanding of Linux, Docker and Docker Compose is needed.<\/p>\n<p>This article assumes that all microservices are packed as docker images and are published into a private or public docker repository.<\/p>\n<p>The end to end test framework must support Junit-like runners that can be launched from Gradle, Maven, or command line.[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row column_structure=&#8221;1_2,1_2&#8243; use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;1_2&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.7.7&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; hover_enabled=&#8221;0&#8243; border_color_left=&#8221;#ff6317&#8243; sticky_enabled=&#8221;0&#8243;]<\/p>\n<h2>Context<\/h2>\n<p>The sample <a href=\"https:\/\/www.berg-software.com\/\">software<\/a> is an enterprise application developed on Java tech stack with Spring Boot on the backend and Angular on the frontend. The system is composed of multiple microservices deployed on Open Telekom Cloud as docker containers. We are using Hazelcast as the integration backbone and MySql as persistent storage.<\/p>\n<p>Because there are multiple environments, we have set up a GitLab pipeline for continuous integration and delivery. We have developed a consistent test suite composed of unit tests, integration tests, security tests, performance tests, and end-to-end tests. Most of them are executed on our environment branches on every merge. We are working with feature branches that get merged into the environment branches. There are basically five environments, two development systems, one feature preview system, one staging system, and the production environment.<\/p>\n<p>As an end-to-end test framework, we are using Serenity because it provides strong support for automated tests using Selenium 2. Serenity\u2019s purpose is to produce living documentation. The tests are written in the give &#8211; when &#8211; then style, and deliver illustrated narrative reports.<\/p>\n<p>The aggregated reports can be organized by using features, stories, steps, scenarios, and tests.<\/p>\n<p>[\/et_pb_text][\/et_pb_column][et_pb_column type=&#8221;1_2&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_image src=&#8221;\/\/cdn.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-01-Interaction-journey.jpg&#8221; alt=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 01 Interaction journey&#8221; title_text=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 01 Interaction journey&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;4vh|||2vw|false|false&#8221;][\/et_pb_image][et_pb_image src=&#8221;\/\/cdn.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-02-Test-count.jpg&#8221; alt=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 02 Test count&#8221; title_text=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 02 Test count&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;4vh|||2vw|false|false&#8221;][\/et_pb_image][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;intro&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row column_structure=&#8221;1_2,1_2&#8243; use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;1_2&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Whatareendtoendtests\">What are end-to-end tests?<\/h2>\n<p>End-to-end tests are basically user interface tests. They are at the top of the test pyramid and are the most expensive to develop and maintain, but the slowest to execute. Therefore, just a small percent of the whole test suite is consisting of end-to-end tests.[\/et_pb_text][\/et_pb_column][et_pb_column type=&#8221;1_2&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_image src=&#8221;\/\/cdn.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-03-End-to-end-tests-pyramid.jpg&#8221; alt=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 03 End-to-end tests pyramid&#8221; title_text=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 03 End-to-end tests pyramid&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;4vh|||2vw|false|false&#8221;][\/et_pb_image][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Whywriteendtoendtests\">Why write end-to-end tests?<\/h2>\n<p>The end-to-end tests are a safety net for refactoring and continuous enhancements. They increase the software quality by giving feedback sooner, before going into production.<\/p>\n<p>Last but not least, end to end tests are a necessity because they increase the team confidence in each delivery.[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Whentowriteendtoendtests\">When to write end-to-end tests?<\/h2>\n<p>Sometimes unit tests are hard to implement or to start with, especially for legacy applications where refactoring is needed in order to make the code more testable. In this case, it is safer to start with end-to-end tests.<\/p>\n<p>Also, end-to-end testing can also be used as acceptance tests. If the UI mockups are clear, they can be written in parallel to the story implementation.[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row column_structure=&#8221;1_2,1_2&#8243; use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;1_2&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howmuchtesttowrite\">How much test to write?<\/h2>\n<p>The end to end tests should be performed only on business-critical user journeys. Only happy paths must be included, meaning that we don\u2019t have to test the error cases or the validations.[\/et_pb_text][\/et_pb_column][et_pb_column type=&#8221;1_2&#8243; _builder_version=&#8221;4.4.1&#8243;][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"GitLabbasics\">GitLab basics<\/h2>\n<p>GitLab is Berg Software&#8217;s choice for hosting the source code, and running the continuous integration and delivery pipelines. Their plans range from free to enterprise. You can create private or public repositories.<\/p>\n<p>Some of GitLab\u2019s nicest out of box features are:<\/p>\n<ul>\n<li>REST APIs<\/li>\n<li>CI\/CD support<\/li>\n<li>protected branches<\/li>\n<li>code review support<\/li>\n<li>predefined variables<\/li>\n<li>custom runners support<\/li>\n<li>integration with different identity providers<\/li>\n<li>fine-grained permissions model<\/li>\n<li>scheduled jobs<\/li>\n<\/ul>\n<p>You can define multiple organizations, each of them with their own projects and repositories.<\/p>\n<p>Jobs can be triggered manually or automatically by other jobs or by user commits. Each job executes a pipeline with multiple stages. Each stage can have its own docker image that can be executed on the latest code extracted from the specified branch.<\/p>\n<p>There are predefined environment variables accessible to the user inside the jobs, like branch name and commit message, which are very helpful during the build. There are also user-defined variables that can store sensitive values, and which can be protected. Only protected branches have access to protected variables.[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;intro&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howtosetuptherunnersandcaching\">How to: set up the runners and caching<\/h2>\n<p>Each job is executed by a certain GitLab runner. The runners are container images executed on the user-provided machines. They pick up jobs and execute them. You can define as many runners as you want. There are also shared runners provided free of charge, but you have to wait for other users\u2019 builds.<\/p>\n<p>We are not using shared runners. However, they can be found in GitLab in the project CI\/CD settings section.[\/et_pb_text][et_pb_image src=&#8221;\/\/cdn.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-04-How-to-set-up-the-runners-and-caching.jpg&#8221; alt=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 04 How to set up the runners and caching&#8221; title_text=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 04 How to set up the runners and caching&#8221; _builder_version=&#8221;4.4.6&#8243; max_width=&#8221;60vw&#8221; custom_padding=&#8221;2vh||2vh|2vw|false|false&#8221;][\/et_pb_image][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Each runner must be registered and configured with certain labels or tags. For each project, we can define which runners should be assigned to execute the pipelines, based on the runners\u2019 tags. You need to obtain a registration token from the GitLab group&#8217;s page in the CI\/CD settings.<\/p>\n<p>We are using a Docker Compose to register and initialize the runners one time:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">version: '2'\nservices:\n  register-runner-main-01:\n    restart: 'no'\n    image: gitlab\/gitlab-runner:alpine\n    volumes:\n    - \/opt\/docker_conf\/runner-main-01:\/etc\/gitlab-runner\n    command:\n    - register\n    - --non-interactive\n    - --locked=false\n    - --name= Group - prj - MAIN - 01\n    - --executor=docker\n    - --output-limit=409600\n    - --docker-image=alpine:latest\n    - --docker-volumes=\/var\/run\/docker.sock:\/var\/run\/docker.sock\n    - --docker-volumes=\/opt\/docker_data\/runner-main-01-cache:\/opt\/docker_data\/runner-main-01-cache\n    - --tag-list=otcmain\n    - --docker-privileged\n    - --docker-cache-dir=\/opt\/docker_data\/runner-main-01-cache\n    - --cache-type=s3\n    - --cache-s3-server-address=10.6.yyy.xx:9000\n    - --cache-s3-access-key=minioxxxxx\n    - --cache-s3-secret-key=yyyy\n    - --cache-s3-bucket-name=runner-main\n    - --cache-shared\n    - --cache-s3-insecure\n    environment:\n    - CI_SERVER_URL=https:\/\/gitlab.com\/\n    - REGISTRATION_TOKEN=\u2026<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The runner is a docker container running on one of our cloud machines. The executed docker image is taken from the docker repository gitlab\/gitlab-runner:alpine.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">- --docker-image=alpine:latest<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The command section defines which command gets executed when starting the container. There is a one-time execution.<\/p>\n<p>We have defined the volumes and mappings for the cache and for the configuration.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">volumes:\n    - \/opt\/docker_conf\/runner-main-01:\/etc\/gitlab-runner\n&nbsp;\n    - --docker-volumes=\/opt\/docker_data\/runner-main-01-cache:\/opt\/docker_data\/runner-main-01-cache\n    - --docker-cache-dir=\/opt\/docker_data\/runner-main-01-cache<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The caching uses an AWS S3-like object storage to store the build dependencies. Minio is used as S3 provider:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">    - --docker-cache-dir=\/opt\/docker_data\/runner-main-01-cache\n    - --cache-type=s3\n    - --cache-s3-server-address=10.6.yyy.xx:9000\n    - --cache-s3-access-key=minioxxxxx\n    - --cache-s3-secret-key=yyyy\n    - --cache-s3-bucket-name=runner-main\n    - --cache-shared\n    - --cache-s3-insecure<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The MinIO container used for caching must run in the same docker network as the runner.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">version: '2'\nservices:\n  minio:\n    image: minio\/minio\n    container_name: minio1\n    restart: unless-stopped\n    volumes:\n    - \/opt\/docker_conf\/minio-etc:\/root\/.minio\n    - \/opt\/docker_data\/minio-data:\/data\n    ports:\n    - \"9000:9000\"\n    environment:\n    - MINIO_ACCESS_KEY=minioak\n    - MINIO_SECRET_KEY=\u2026\n    command: server \/data\nnetworks:\n  default:\n    external:\n      name: prj-bridge-network<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]A tag list flag labels the runner; it can be used to map the runner with a certain project.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">- --tag-list=otcmain<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]After initialization, we can start the runners using Docker Compose on our code quality machines:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">version: '2'\nservices:\n  runner-main-01:\n    restart: unless-stopped\n    image: gitlab\/gitlab-runner:alpine\n    container_name: runner-main-01\n    volumes:\n    - \/opt\/docker_conf\/runner-main-01:\/etc\/gitlab-runner\n    - \/opt\/docker_data\/runner-main-01-cache:\/opt\/docker_data\/runner-main-01-cache\n    - \/var\/run\/docker.sock:\/var\/run\/docker.sock<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Each runner has its own cache and config folder, previously configured on the registration process.<\/p>\n<p>In order to not block the main runners on the long execution of the end to end tests:<\/p>\n<ul>\n<li>we have to register and start in the same way as above;<\/li>\n<li>the trigger runners need to start the non-blocking execution of the pipeline from another project pipeline.<\/li>\n<\/ul>\n<p>The startup script looks the same as above:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">services:\n  runner-guitrigger-01:\n    restart: unless-stopped\n    image: gitlab\/gitlab-runner:alpine\n    container_name: runner-guitrigger-01\n    volumes:\n    - \/opt\/docker_conf\/runner-guitrigger-01:\/etc\/gitlab-runner\n    - \/opt\/docker_data\/runner-guitrigger-01-cache:\/opt\/docker_data\/runner-guitrigger-01-cache\n    - \/var\/run\/docker.sock:\/var\/run\/docker.sock<\/pre>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;intro&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howtosetupthepipeline\">How to: set up the pipeline<\/h2>\n<p>Each project lives in its own git repository. In the project root folder, there is a file named .gitlab-ci.yml that configures the pipeline for each project. The file defines variables, cache settings, before and after scripts to be executed and the stages of the pipeline. The stages are basically the steps to be executed during the pipeline, and the conditions for the execution to be triggered. The last step of our pipeline is the end to end test stage.[\/et_pb_text][et_pb_image src=&#8221;\/\/cdn.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-05-How-to-set-up-the-pipeline.jpg&#8221; alt=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 05 How to set up the pipeline&#8221; title_text=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 05 How to set up the pipeline&#8221; _builder_version=&#8221;4.4.6&#8243; max_width=&#8221;60vw&#8221; custom_padding=&#8221;2vh||2vh|2vw|false|false&#8221;][\/et_pb_image][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]We have five environments, each of them with its specific protected branch. When a certain commit is performed there, the application is built and deployed on that specific environment.  For the production environment, we have to create a certain tag with a specific naming convention for deployment.<\/p>\n<p>You can define variables that can be used in the same file, or in all pipeline scripts:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">variables:\nGRADLE_OPTS: \"-Dorg.gradle.daemon=false\"\nDOCKER_DRIVER: overlay2<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The stages are defined as follows: [\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">stages:\n  - opssetupchmodfix\n  - test<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Here we have two stages. Not all stages are executed in one single run. We will detail each stage definition later on.<\/p>\n<p>To speed up the jobs execution time, we need to cache the dependencies of each build so they are not downloaded every time. The cache has a key that is picked from the predefined git environment variable CI_COMMIT_REF_SLUG, that contains the git tag or branch name for which the project is built:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family: monospace;\">&lt;pre class=\"text\" style=\"font-family:monospace;\"&gt;cache:\n key: ${CI_COMMIT_REF_SLUG}\n paths:\n - .gradle\/wrapper\n - .gradle\/caches&lt;\/pre&gt;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]We use per branch cache. All dependencies are cached inside the project folder, and the cache can be reused on every job execution within the specific project. At the end of the job, the specified folders are zipped into an archive cache.zip and stored under the specified key on the machine where the GitLab runner is executed.<\/p>\n<p>We can define a script to be executed before starting the execution of the stages:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">before_script:\n  - export GRADLE_USER_HOME=`pwd`\/.gradle<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The test stage labeled \u2018guitest\u2019 starts the execution of the end to end tests:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">guitest:\n  image: registry1.projekt.de\/prj-dind-chrome\n  stage: test   \n  script:\n    - .\/ci\/bin\/guitest.sh \"$EXECUTE_TEST_FOR_ENVIRONMENT\" \"$SRC_TRGR_BRANCH\"\n  tags:\n    - guirunner<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The test stage is associated with certain runners, based on tag label. Only those runners that have the same tag \u2018guirunner\u2019 can pick up the execution of this stage.<\/p>\n<p>The image registry1.projekt.de\/prj-dind-chrome is a custom docker image based on selenium\/standalone-chrome:latest enhanced with bash, curl, openssl, git, x11 server, jdk and other tools, in order to be able to execute the end to end test in a browser environment.<\/p>\n<p>Excerpt from the Dockerfile used to build the image:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">RUN sudo apt-get -y install bash curl git openssl openssh-client openjdk-8-jdk libxpm4 libxrender1 libgtk2.0-0 libnss3 libgconf-2-4 xvfb gtk2-engines-pixbuf xfonts-cyrillic xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable x11-xserver-utils x11-xkb-utils<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The runner executing this stage will start the given container from the defined image; will check out the code of the end to end project; and will launch (inside the container) the script defined in the script section:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">script:\n     .\/ci\/bin\/guitest.sh \"$EXECUTE_TEST_FOR_ENVIRONMENT\" \"$SRC_TRGR_BRANCH\"<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The script is obtained from the checked-out source code. There are two environment variables given to the script that are provided to the container by the caller. They are computed based on the current branch or tag variable provided by GitLab CI_COMMIT_REF_NAME. I will explain the script later on.<\/p>\n<p>The process that triggers the execution can be another pipeline that calls the command in a stage[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">trigger -a ${GITLABTRIGGER_APITOKEN} -p ${ GITLABTRIGGER_GUITESTTOKEN} -t ${branch} ${GITLABTRIGGER_GUITESTID} \n-e SRC_TRGR_BRANCH=${CI_COMMIT_REF_NAME} \n-e EXECUTE_TEST_FOR_ENVIRONMENT=${GUITEST_ENVIRONMENT^^} \n-e EXECUTE_TEST_FOR_PIPELINE=${CI_PIPELINE_URL}<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The command is available in the docker image registry.gitlab.com\/finestructure\/pipeline-trigger.<\/p>\n<p>The artifacts section labeled \u2018artifacts:\u2019 defines what files should be made available for download after the job finishes, what is the source of those files, and for how long they should be preserved.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">artifacts:\n    when: always\n    paths:\n      - target\/*\n      - \/opt\/selenium\/config.json\n    expire_in: 1 week\n<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The stage is executed only for certain branches defined in the \u2018only:\u2019 section. Additionally, the stage is not executed when the commit message given by the developer contains the text \u2018opssetup\u2019 expressed as a regular expression. There is a logical \u2018and\u2019 condition between the two.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">only:\n    - branch_dev1\n    - branch_dev2\n    - branch_preview\n    - master\n    - \/^story-\/\n  except:\n    variables:\n      - $CI_COMMIT_MESSAGE =~ \/^opssetup-*\/\n<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The script guitest.sh can launch the end to end test inside the runner container or on another dedicated remote machine. We started with the first approach but ended with running the tests on a dedicated machine.<\/p>\n<p>The first approach used the commands:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">Xvfb -ac :99 -screen 0 1280x1024x16 &amp;\nexport DISPLAY=:99\n\n.\/gradlew -i clean runAParallelSuite aggregate -P webdriver.base.url=$TARGET_BASE_URL\n<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The first step was to set up the in-memory display server and resolution. In the second step, we are launching a custom Gradle task. More details will follow.<\/p>\n<p>This approach doesn\u2019t scale very well. We got frequent memory issues so we decided to start the tests on a dedicated machine using the commands:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">openssl enc -aes-XXalgo -d -pass env:GRP_SSL_ENCSECRET -in ci\/secrets-enc\/id_rsa-usr-dockerexec.dat &gt;| ci\/secrets\/id_rsa-usr-dockerexec\n\nssh -i ci\/secrets\/id_rsa-usr-dockerex usr-dockerex@10.6.xx.xx  -o StrictHostKeyChecking=no \"\/opt\/docker_exec\/launch-tests.sh ${CI_JOB_ID} ${TARGET_LABEL} ${TARGET_BRANCH} ${TARGET_ENV}\"\n<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]With the help of Ansible, we have set up the machine with the right user accounts and permissions to be able to launch the GUI tests from a remote machine. The user secret key is provided after it is decrypted using a protected environment variable GRP_SSL_ENCSECRET defined in the GitLab user interface. The script variables were computed based on the branch name and default GitLab variables.<br \/>\nEach job gets assigned a unique id exposed as CI_JOB_ID. The TARGET_LABEL is used to indicate the docker image labels to be used when running the test. The other two variables are indicating the branch from which to check out the test code and the environment to consider when downloading the images. In a further section, we will explain the launch script.<\/p>\n<p>Another stage called \u2018opssetupchmodfix\u2019 is executed only when the provided commit message contains the text \u2018opssetup-chmod-fix\u2019, and only on the runner labeled with \u2018otcprj\u2019. The stage changes the permissions of the executable bash files checked out from git into the current project workspace folder and pushes the changes back to git. The default permissions are denying the execution.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family: monospace;\">opssetup-chmod-fix:\n  stage: opssetupchmodfix\n  image: registry1.projekt.de\/prj-dind-base\n  script:\n    - chmod oga+x .\/ci\/bin\/*.sh\n    - .\/ci\/bin\/fixexecuteflag.sh\n  tags:\n    - otcprj\n  only:\n    variables:\n      - $CI_COMMIT_MESSAGE =~ \/^opssetup-chmod-fix*\/\n<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The stage is needed to be executed once when coming from windows environment because there is no easy way to set the execution permission for the bash files.[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;intro&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howtoisolatethetests\">How to: isolate the tests<\/h2>\n<p>You might think that isolating the tests is a bad thing because you don\u2019t get feedback from the real system, data, and users. But actually, most of the time, the purpose of the end to end tests is to verify that the existing business-critical functionality still works as expected after performing any changes. Verifying the system consistency or performance must not be done using the end to end tests.[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howtopreparetheenvironmentforrunningthetests\">How to: prepare the environment for running the tests<\/h2>\n<p>We have created a cloud machine for running the end to end tests using terraform scripts. If multiple machines are needed, they can be created dynamically. The machine is configured using Ansible. The user \u2018usr-dockerex\u2019 is created and allowed (A) to connect via its ssh key, and (B) to execute the end to end tests launch script. The script launch-tests.sh is copied during the machine setup.<\/p>\n<p>We need to be able to run in parallel multiple end-to-end tests for different branches and environments. Therefore, we are using the job id to prepend all services started by one run. We are using Docker Compose to start the containers.<\/p>\n<p>To each container name we append the job ID like in the following:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">container_name: ui-client${JOB_ID}<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]We create a docker image in the pipeline of each microservice, and push it to our docker repository. From there, the end to end tests are pulling and starting the images. Different branches produce different docker images, with well-defined labels.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">image: \"registry1.projekt.de\/ui-client:${ CLIENT_IMAGE_TAG}\"<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The tag is computed using GIT API based on the source branch, target environment, and the project that triggered the build. When working on a feature, multiple microservices and UI clients might be affected.<\/p>\n<p>The microservices that are not changed have to be started as well for the end to end test. For this, we need to specify a target environment where to get the latest images for that environment. Each environment is bound to a protected branch.<\/p>\n<p>Let\u2019s suppose we have 3 microservices and only 2 are changed. Each one resides in its own git repository. The changes for the same story\/feature are performed in a branch with the same name, let\u2019s say feature1. The first environment where the feature is deployed should be the dev environment. So, the unchanged microservice3 has to use the latest image built for the dev environment. The used images are in this case the following:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">\u2022\timage: \"registry1.projekt.de\/microservice1:feature1\" \n\u2022\timage: \"registry1.projekt.de\/ microservice2:feature1\" \n\u2022\timage: \"registry1.projekt.de\/ microservice3:dev1_latest\"<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]If feature1 branch would exist for the microservice3, it would also be used for the end to end tests. We check the existence of a branch with the following script:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">function check_branch_existence(){\n     local result=$1\n    local project=$2\n    local branch=$3\n    local exists=$(curl -I -so \/dev\/null -w \"%{http_code}\" \"https:\/\/gitlab.com\/api\/v4\/projects\/${project}\/repository\/branches\/${branch}?private_token=${GRP_GITUSER_ACCESSTOKEN}\")\n    eval $result=\"'$exists'\"\n}\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]If the branch exists, the output result variable should contain the 204 HTTP response code.<\/p>\n<p>We have to check the existence of a docker image tag to launch the right container or fall back to a default image which is actually the dev image. We use the following function:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">function check_image_tag_existence(){\n    local result=$1\n    local project=$2\n    local image_tag=$3\n    local repo=$4\n    echo \"docker inspect --type=image ${repo}\/${project}:${image_tag}\"\n    local exists=$(docker inspect --type=image \"${repo}\/${project}:${image_tag}\" --format \"{{.Id}}\" &gt; \/dev\/null 2&gt;&amp;1 &amp;&amp; echo $? || echo $?) \n    echo \"Exists image? ${exists}\"\n    if [ $exists -eq 1 ]; then\n\techo \"try to pull image\"\n\tlocal pullresult=$(docker pull \"${repo}\/${project}:${image_tag}\";echo $?)\n\techo \"pull result ${pullresult}\"\n\techo \"tried to pull image ${repo}\/${project}:${image_tag}\"\n    fi\n    exists=$(docker inspect --type=image \"${repo}\/${project}:${image_tag}\" --format \"{{.Id}}\" &gt; \/dev\/null 2&gt;&amp;1 &amp;&amp; echo $? || echo $?) \n    echo \"after second inspect $exists\"\n    eval $result=\"'$exists'\"\n}<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]If the image is not downloaded locally the docker inspect command won\u2019t find it. Therefore, a pull is performed before the second inspect command. The result of the second inspect is returned.<\/p>\n<p>We are iterating through all our GitLab microservices projects to compute the right environment variables with the container images and tags.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">projects=(\"1233xxx\" \"1224xxxx\"\u2026 )\nproject_names=(..)\nproject_vars=(..)\nfor i in ${!projects[@]};\n\tdo\ncheckImageTag ${projects[$i]} ${project_names[$i]} ${project_vars[$i]} $imageTag \"dockerregistry1.server\" \"dev1-latest\"\n\tdone\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The function checkImageTag exports the right environment variables for each microservice, or default them.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">function checkImageTag(){\n\t    local curProjId=$1\n\t\tlocal curProjName=$2\n\t\tlocal curProjVar=$3\n\t\tlocal curProjTag=$4\n\t\tlocal curRegistry=$5\n\t\tlocal envBranch=$6\n\t\tcheck_image_tag_existence imageexistresult $curProjName $curProjTag $curRegistry\n\t\tif [ $imageexistresult -eq 0 ]; then \n\t\t\teval \"export $(echo ${curProjVar}_IMAGE_TAG | tr [a-z] [A-Z])=$imageTag\"\n\t\telse\n\t\t\t#fallback to the default tag\n\t\t\tcheck_image_tag_existence imageexistresult $curProjName $envBranch $curRegistry\n&nbsp;\n\t\t\tif [ $imageexistresult -eq 0 ]; then \n\t\t\t\teval \"export $(echo ${curProjVar}_IMAGE_TAG | tr [a-z] [A-Z])=dev1-latest\"\n\t\t\telse\n\t\t\t\techo \"image ${curProjTag} for project ${curProjName} does not exist!\"\n\t\t\t\texit 1\n\t\t\tfi\n\t\tfi\n}<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]We will have exported for each microservice one variable, in the form of SERVICE_VAR_NAME_IMAGE_TAG. The var name by convention is the upper case of service name with the minus character replaced by an underscore.<\/p>\n<p>So far, we have computed (A) the container names to be unique for different jobs, and (B) the right docker image tags based on the branch name and target environment. We still miss the proper cleanup before launching the new containers for the tests.<\/p>\n<p>The execution of the end to end tests are taking less than one hour so we can stop all containers started more than one hour ago, that are still running. The containers have the prefix ad. We filter those containers and count them. If there are such containers, we use docker stop giving the list of filtered containers IDs.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">if [ `docker ps --filter \"status=running\" --filter \"name=ad\" | grep 'hour.* ago' | awk '{print $1}' | wc -l ` -gt 0 ] ; then\ndocker stop $(docker ps --filter \"status=running\" --filter \"name=ad\" | grep 'hour.* ago' | awk '{print $1}')\nfi<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]We will delete the containers afterward.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">if [ `docker ps --filter \"status=exited\" | grep 'hour.* ago' | awk '{print $1}' | wc -l ` -gt 0 ] ; then\ndocker rm $(docker ps --filter \"status=exited\" | grep 'hour.* ago' | awk '{print $1}')\nfi<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Depending on the resources of test machine(s), we can limit the amount of tests to run in parallel on one machine. It does not matter which service is counted, it can be any.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">if [ `docker inspect --format='{{.Name}} ' $(docker ps -aq --filter \"status=running\" --filter \"name=admicroservice1*\" )  | cut -d r -f 3 | wc -l` -lt 5 ] ; then\n echo 'allowing not more than 5 running gui tests'\nelse\n echo 'there are running in parallel already 5 gui test, please retry later '\n exit 1\nfi<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]A new network is created for every job ID.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">export NETWORK_BRIDGE=nwkbguitest${JOB_ID}\n[ ! \"$(docker network ls | grep $NETWORK_BRIDGE)\" ] &amp;&amp; docker network create -d bridge $NETWORK_BRIDGE || echo \"Network present!\"\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]If the containers are already running for the same job id, we stop and remove them via the following commands:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">vardockers=`docker ps -a --filter \"name=*${JOB_ID}\"| wc -l`\nif [ $vardockers -eq 1 ]\nthen\n echo \"no containers\"\nelse\n        echo \"stop and remove all containers\"\n        docker stop $(docker ps -a -q --filter \"name=*${JOB_ID}\")\n        docker rm $(docker ps -a -q --filter \"name=*${JOB_ID}\")\nfi<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]After the initial cleanup is performed, we are starting all containers with Docker Compose in the background.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">docker-compose -f \/opt\/docker_compose\/docker-compose-apps.yml up -d<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Afterwards, containers\u2019 IPs are logged to console and the log output of the end to end test container is redirected to the console.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">docker inspect --format='{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)\ndocker logs -f endtoend-tests${JOB_ID}<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]When the tests are finished, the final cleanup will be performed and the result code will be evaluated and returned.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">result=`docker inspect $(docker ps -aq --filter \"name=endtoend-tests${JOB_ID}\") --format='{{.State.ExitCode}}'`\ndocker-compose -f \/opt\/docker_compose\/docker-compose-apps.yml down\ndocker network rm $NETWORK_BRIDGE\ndocker network prune -f\ndocker image prune -f\ndocker volume prune -f\nexit $result<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The Docker Compose .yml file will be explained in a further section.[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;intro&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howtopreparethedataforthetests\">How to: prepare the data for the tests<\/h2>\n<p>We use the Spock framework for system testing of our rest API. Spock is a testing and a specification platform for Java and Groovy applications that can be easily integrated with IDEs and CI pipelines via the Junit runner. Spock allows us to write specifications that describe expected features from a system under test. The tests can produce documentation for a wider audience than developers, by using the labeled blocks \u201cgiven\u201d, \u201cwhen\u201d and \u201cthen\u201d (like in the following):[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221; locked=&#8221;off&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">given: \"an admin user\"\n\/\/ ...code\nwhen: \"his company settings are changed\"\n\/\/ ...code\nthen: \"the user is notified\"\n\/\/...code<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243; locked=&#8221;off&#8221;]The produced reports are looking like this:[\/et_pb_text][et_pb_image src=&#8221;\/\/cdn.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-06-Reports.jpg&#8221; alt=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 06 Reports&#8221; title_text=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 06 Reports&#8221; _builder_version=&#8221;4.4.6&#8243; max_width=&#8221;60vw&#8221; custom_padding=&#8221;2vh||2vh|2vw|false|false&#8221; locked=&#8221;off&#8221;][\/et_pb_image][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243; locked=&#8221;off&#8221;]The tests are organized in test suites and specs. Each spec deals with a particular domain of our APIs.[\/et_pb_text][et_pb_image src=&#8221;\/\/cdn.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-07-Reports-scaled.jpg&#8221; alt=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 07 Reports&#8221; title_text=&#8221;Berg Software &#8211; End-to-end tests an GitLab integration &#8211; 07 Reports&#8221; _builder_version=&#8221;4.4.6&#8243; max_width=&#8221;60vw&#8221; custom_padding=&#8221;2vh||2vh|2vw|false|false&#8221; locked=&#8221;off&#8221;][\/et_pb_image][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243; locked=&#8221;off&#8221;]Basically, we are using the system tests to populate an empty database on which to run the end to end tests. A small number of system tests are written specifically for the end to end testing. This is a necessity where some data is needed, or some workflows must be prepared in advance because the same cannot be created just from the end to end tests. The tests created to facilitate the end to end tests execution should be clearly distinguished.<\/p>\n<p>Specific users are used for testing, in order to isolate (as much as possible) the interferences with the existing users active on the system.<\/p>\n<p>At the beginning of the execution, the test data is prepared. In the end, the cleanup is performed.<\/p>\n<p>The Docker Compose file from above contains the MySQL service that executes an SQL script on startup, to create the empty databases.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">db-mysql:\n        container_name: prj-mysql${JOB_ID}\n        image: mysql:5.8\n        volumes:\n            - \".\/setmode.cnf:\/etc\/mysql\/conf.d\/setmode.cnf\"\n        ports:\n            - \"3306\"\n        entrypoint:\n             sh -c \"\n               echo 'CREATE DATABASE IF NOT EXISTS at_projects  \/*!40100 DEFAULT CHARACTER SET utf8 *\/; CREATE DATABASE IF NOT EXISTS at_server; \u2026' &gt; \/docker-entrypoint-initdb.d\/init.sql;\n               \/usr\/local\/bin\/docker-entrypoint.sh --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci\n             \"<\/pre>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howtorunthetestswithdockercompose\">How to: run the tests with docker compose<\/h2>\n<p>All microservices and required runtime dependencies are declared in the Docker Compose file. The container names are unique, while the image tags are computed from the launching script and provided as environment variables.<\/p>\n<p>All services for one job are started in the same network including the database server.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">networks:\n  default:\n    external:\n      name: ${NETWORK_BRIDGE}<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Each container must have some resource limits specified (like memory and CPU).<\/p>\n<p>A typical back end service looks like this:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">services: \n prj-server:\n    image: \"registry1\/prj-server:${PRJ_SERVER_IMAGE_TAG}\"\n    container_name: prj-server${JOB_ID}\n    environment:\n      - \"SPRING_PROFILES_ACTIVE=otcdev,swagger\"\n      - \"SQL_SERVER_IP=db-mysql${ JOB_ID}\"\n\u2026\n    ports:\n      - 8080\n    restart: always\n    mem_limit: 2500m\n    depends_on:\n        - db-mysql<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]A typical front end service looks like this:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">ui-client:\n    image: \"registry1 \/ui-client:${UI_CLIENT_IMAGE_TAG}\"\n    container_name: ui-client${JOB_ID}\n    environment:\n      - \"BACKEND_SERVER_URL=http:\/\/prj-server${ JOB_ID}:8080\"\n      - \"BACKEND_CHECKLIST_URL=http:\/\/prj-checklists${JOB_ID}:8084\"\n    ports:\n      - 8085\n    restart: always\n    command: \/bin\/bash -c \"envsubst &lt; \/opt\/enviroment-endpoints.template &gt; \/opt\/enviroment-endpoints.json &amp;&amp; cp \/opt\/enviroment-endpoints.json \/usr\/share\/nginx\/html\/assets\/enviroment-endpoints.json &amp;&amp; exec nginx -g 'daemon off;'\"<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]One interesting thing is the substitutions of the backend URLs environment variables into a file provided to the nginx web server. This file is then included in the UI image, to point the UI of the end-to-end tests to the right backend.  At this point, we only need an instance for each service.<\/p>\n<p>Running the test is actually performed in a special container started from a chrome image. The image with all dependencies set is prepared to run the end-to-end test.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">services:\n endtoend-tests:\n    image: registry1.projekt-adis.de\/adis-dind-chrome\n    container_name: endtoend-tests${JOB_ID}\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The section maps a host folder to a container folder. The host folder will be persistent even after the container finishes the work. This is where we will store the end to end test reports.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">volumes:\n      - \/opt\/docker_data\/guitest:\/repo\/tests<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The entry point defines the command to run when starting the container.  There are multiple Linux commands separated by ;[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">entrypoint: &gt;\t\t\t\t\t           \n        \/bin\/sh -c \"<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]First, the git user is configured to appear when committing.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family: monospace;\">git config --global user.name \\\"${GRP_GITUSER_NAME}\\\";\t\ngit config --global user.email \\\"${GRP_GITUSER_EMAIL}\\\";\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Unique folders for each job id are created, and access rights are granted.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family: monospace;\">sudo mkdir repo;\nsudo chmod a+rwx repo;\ncd repo;\nsudo mkdir tests\/${JOB_ID};\nsudo chmod a+rwx tests\/${JOB_ID};\nsudo rm -rf adis-systemtest;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The code for the system test is checked out and executed on the fresh environment, in order to populate the end to end test data.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">git -C . clone --branch ${PRJ_SYSTEMTEST_BRANCH} https:\/\/${GRP_GITUSER_LOGON}:${GRP_GITUSER_ACCESSTOKEN}@gitlab.com\/company\/projects\/cust\/prj\/prj-systemtest.git;\n        chmod oga+x .\/prj-systemtest\/ci\/bin\/systemtest.sh;\n        cd prj-systemtest;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The tests for populating the data are launched, giving them the right back end URLs.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">.\/ci\/bin\/systemtest.sh ISOLATED http:\/\/prj-server${JOB_ID}:8080 http:\/\/prj-projects${ JOB_ID}:8081 http:\/\/prj-checklists${ JOB_ID }:8084;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The test results are parsed, and the job is failed if there are failures.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family: monospace;\">cp -R build\/test-results\/test \/repo\/tests\/${ JOB_ID}\/test-results-system;\ncat \/repo\/tests\/${ JOB_ID}\/test-results-system\/TEST-specs.TestSuite.xml;\ncat \/repo\/tests\/${JOB_ID}\/test-results-system\/*.xml &gt;&gt; \/repo\/tests\/${ JOB_ID}\/test-results-system\/systestresult.log;\n[ `cat \/repo\/tests\/${JOB_ID}\/test-results-system\/systestresult.log | grep '\\failures=.[^0].' | wc -l` -eq 0 ] &amp;&amp; echo 'system test success' || exit 1;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The end to end tests are checked out from git (i.e. from the computed branch).[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family: monospace;\">cd ..;\nsudo rm -rf prj-guitest;\ngit -C . clone --branch ${PRJ_GUITEST_BRANCH} https:\/\/${GRP_GITUSER_LOGON}:${GRP_GITUSER_ACCESSTOKEN}@gitlab.com\/company\/projects\/client\/prj\/prj-guitest.git;\nchmod oga+x .\/prj-guitest\/ci\/bin\/*.sh;\ncd prj-guitest;\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The end to end test is launched, also providing the URLs of the user interface clients.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">.\/ci\/bin\/guitest.sh ISOLATED http:\/\/prj-client${ JOB_ID} http:\/\/prj-admin-client${ JOB_ID};<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]The results are evaluated, parsed, and copied to a location where the nginx server can serve them to the outside.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">cp -R target\/site \/repo\/tests\/${ JOB_ID}\/test-site;\n        cp -R build\/test-results\/runAParallelSuite \/repo\/tests\/${ JOB_ID }\/test-results;\n        cp -R build\/reports\/tests\/runAParallelSuite \/repo\/tests\/${ JOB_ID }\/test-reports;\n        cat \/repo\/tests\/${ JOB_ID }\/test-site\/serenity\/results.csv;\n        [ `tail -n +2 \/repo\/tests\/${ JOB_ID }\/test-site\/serenity\/results.csv | grep -v SUCCESS | wc -l` -eq 0 ] &amp;&amp; echo 'gui test successful' || exit 1;\n        \"\n    mem_limit: 7500m<\/pre>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221; locked=&#8221;off&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howtocleanupafterthetests\">How to: clean up after the tests<\/h2>\n<p>After running the test suites, we execute the cleanup step. The cleanup can fail because of various reasons, such as abrupt program termination due to the lack of resources, corrupted data or network failure, bad cleanup procedure, or redeployment in progress.  One way to deal with the situation is to also do the cleanup at the beginning of the test.  If the cleanup does not succeed, then fail the test. Sometimes you cannot delete any created resources if the delete API is not available or the cleanup is too expensive.<\/p>\n<p>Our solution is to always use a clean database for each end-to-end test execution and to populate it with the system tests. In order to do that, we needed an isolated environment.[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221; locked=&#8221;off&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howtomakethetestsasfastaspossible\">How to: make the tests as fast as possible<\/h2>\n<p>The end-to-end tests are executed in parallel, to minimize execution time. In order to do that, the tests are split into independent test suites. This means that the data consumed or produced by one suite does not alter the data of other suites running in parallel. Pay attention when certain users\u2019 actions impact all other users\u2019 running sessions. For instance: the user language is changed, the user preference to not display the menu is changed, the privileges are changed.<\/p>\n<p>As seen above, there is a Gradle task that extends the standard test task to which we provide the application URLs where to point the browser:<\/p>\n<p>For the standard test task (i.e. the one where the browser should point to, based on the application\u2019s URLs), there is a Gradle task that extends it.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">task runAParallelSuite(type: Test) {\n    systemProperty \"webdriver.base.url\", findProperty(\"webdriver.base.url\")\n    maxParallelForks = 3\n    forkEvery = 1\n    include '**\/**TestSuite.class'\n    testLogging.showStandardStreams = true\n}\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]There are 3 test suites to be executed in parallel, on three threads.<\/p>\n<p>Other factors to influence the performance are the logging, and the capturing of screenshots. We have reduced the logging to an acceptable level, and have set up the screenshots to only be captured in case of failure. The serenity.properties file, located in the root of the end to end project, contains the configurations:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">serenity.take.screenshots=FOR_FAILURES\nserenity.logging=NORMAL<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]In order to not interfere with the browser windows, running in headless mode is also a requirement. On Linux a virtual terminal has to be set up with a certain resolution, as shown above.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">chrome.switches=--headless --disable-extensions --disable-gpu --disable-dev-shm-usage --no-sandbox<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]At some point, we got a lot of invalid session errors and headless mode, that were fixed by adding the two flags &#8211;disable-gpu &#8211;disable-dev-shm-usage.<\/p>\n<p>Inside the end-to-end tests xpaths based on ids have to be used for maximum performance.[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221; locked=&#8221;off&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Howtomaintainthetestsuite\">How to: maintain the test suite<\/h2>\n<p>User journeys are written for business-important scenarios. Here, the user performs different actions, to achieve a certain goal. The tasks are performed in the user interface by interacting with it. The test asks questions or makes assumptions about the result of the visible task.<\/p>\n<p>Group the code into reusable tasks or actions performed by the user:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">public abstract class BaseLoginTask implements Task\npublic class OpenMyProfilePage implements Task\npublic class EnterValueIntoField implements Task\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Write reusable user interface questions or queries:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">public class ConfirmationMessageAppears implements Question&lt;String&gt;\npublic class ValueInColumnOfTable implements Question&lt;List&lt;String&gt;&gt;\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]In order to have a maintainable test suite, one important thing is to use xpaths based on ids and not on parent\/child relations within the HTML. The changes to the layout are performed very often and should not influence the end to end tests. Use constructs like this:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">public static Target ADD_TASK = Target.the(\"AddTask button\").located(By.id(\"btn-add-tasks\"))<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]instead of:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">Target.the(\"The second selected interaction\").locatedBy(\"\/\/div\/span\/app-link-or-text\/a\");<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Another thing is to not introduce hard-coded wait times (like \u201cwait for 20 seconds\u201d). Instead, use constructs that wait for certain conditions to happen and provide a timeout: [\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">WaitUntil.the(ChecklistTemplates.ADD_CHECKLIST_TEMPLATE_BUTTON,             WebElementStateMatchers.isEnabled()) \n.forNoMoreThan(TimeConstants.SECONDS_10). seconds().performAs(actor);\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]Several levels of time outs should be established as constants, to be used everywhere. Also, a factor to adjust the timeouts at once should be established and given as system properties when running in a slower environment.[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">public static final int SECONDS_1 = 1 * factor;\n    public static final int SECONDS_3 = 3 * factor;\n    public static final int SECONDS_10 = 10 * factor;    \n    public static final int LOADER = 5 * factor; \n    public static final int LONG_LIST = 10 * factor;\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]In serenity.properties we have:[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family:monospace;\">#How long webdriver waits for elements to appear by default, in milliseconds.\nwebdriver.wait.for.timeout=5000\n#How long webdriver waits by default when you use a fluent waiting method, in milliseconds.\nwebdriver.timeouts.implicitlywait=5000\n#How long should the driver wait for elements not immediately visible, in milliseconds.\nserenity.timeout=5000\n&nbsp;<\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]For slow pages, if possible, we advise introducing a progress bar or a loader indicator in the user interface, that can be checked at the beginning and end of long-running operations. [\/et_pb_text][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1em&#8221; text_line_height=&#8221;1.3em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;1em&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; background_color=&#8221;#f8f8f8&#8243; custom_margin=&#8221;|5vw||5vw|false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_width_left=&#8221;1px&#8221; border_color_left=&#8221;#bbbbbb&#8221;]<\/p>\n<pre class=\"text\" style=\"font-family: monospace;\">WaitUntil.the(MainUIUtilities.LOADER, WebElementStateMatchers.isVisible());\nWaitUntil.the(MainUIUtilities.LOADER, WebElementStateMatchers.isNotPresent()).\nforNoMoreThan(TimeConstants.SECONDS_10).seconds().performAs(actor);\n&nbsp;<\/pre>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;intro&#8221; _builder_version=&#8221;4.4.6&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row use_custom_gutter=&#8221;on&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;6vh||||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.1em&#8221; text_line_height=&#8221;1.6em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;1.5em&#8221; header_2_line_height=&#8221;0.9em&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2 id=\"Conclusions\">Conclusions<\/h2>\n<ul>\n<li>Private git runners have to be registered for job execution<\/li>\n<li>Minio can be used as a pipeline cache provider<\/li>\n<li>The end to end tests must have their own GitLab project set up.<\/li>\n<li>The execution can be triggered from any project from a stage defined in gitlab-ci.yml file, using the pipeline-trigger container image.<\/li>\n<li>The tests should be executed on a dedicated\/provisioned environment, isolated from external influence for better stability.<\/li>\n<li>GitLab API can be leveraged for complex situations<\/li>\n<li>The tests should be broken into independent test suites that must run in parallel, headless, with screenshots on failure only, for achieving the best performance<\/li>\n<li>User journeys must be defined to test only the business-critical happy paths.<\/li>\n<li>The backend integration tests can be used to populate the data for the end to end tests.<\/li>\n<li>For maintainability, ids must be used everywhere in UI in addition to the code review and code reuse practices.<\/li>\n<li>Wait for condition, with a timeout, must be used instead of fixed wait time.<\/li>\n<li>A cleanup of the test data should be performed on every run<\/li>\n<\/ul>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; _builder_version=&#8221;4.4.1&#8243; custom_padding=&#8221;|||0px||&#8221; locked=&#8221;off&#8221;][et_pb_row use_custom_gutter=&#8221;on&#8221; gutter_width=&#8221;3&#8243; admin_label=&#8221;\uff3f&#8221; _builder_version=&#8221;4.4.6&#8243; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;4vh||8vh||false|false&#8221; border_color_left=&#8221;rgba(0,0,0,0)&#8221;][et_pb_column type=&#8221;4_4&#8243; _builder_version=&#8221;4.4.6&#8243;][et_pb_text _builder_version=&#8221;4.4.6&#8243; text_font=&#8221;|300|||||||&#8221; text_font_size=&#8221;1.12em&#8221; text_line_height=&#8221;1.55em&#8221; quote_font=&#8221;|700|||||||&#8221; quote_text_align=&#8221;left&#8221; quote_font_size=&#8221;16px&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;30px&#8221; header_3_font_size=&#8221;23px&#8221; header_4_font=&#8221;||||||||&#8221; header_4_font_size=&#8221;16px&#8221; header_4_line_height=&#8221;1.5em&#8221; header_5_font_size=&#8221;14px&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;||||false|false&#8221; border_color_left=&#8221;#ff6317&#8243;]<\/p>\n<h2>\uff3f<\/h2>\n<p>How do you approach end-to-end testing \/ pipeline integration? Got any experience that you can share? <a href=\"https:\/\/berg-software.com\/contact-berg-software\/\" target=\"_blank\" rel=\"noopener noreferrer\">Let us know<\/a>![\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; _builder_version=&#8221;3.22&#8243; background_color=&#8221;#eeeeee&#8221; custom_padding=&#8221;50px||50px||false|false&#8221; border_color_top=&#8221;#ff6317&#8243; global_module=&#8221;1642&#8243;][et_pb_row column_structure=&#8221;1_3,1_3,1_3&#8243; _builder_version=&#8221;4.4.1&#8243; custom_padding=&#8221;0px|||||&#8221; locked=&#8221;off&#8221;][et_pb_column type=&#8221;1_3&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_social_media_follow _builder_version=&#8221;4.4.4&#8243; text_orientation=&#8221;left&#8221;][et_pb_social_media_follow_network social_network=&#8221;linkedin&#8221; url=&#8221;https:\/\/www.linkedin.com\/company\/berg-computers-srl\/&#8221; _builder_version=&#8221;4.4.4&#8243; background_color=&#8221;#007bb6&#8243; follow_button=&#8221;off&#8221; url_new_window=&#8221;on&#8221;]linkedin[\/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network=&#8221;twitter&#8221; url=&#8221;https:\/\/twitter.com\/berg_software&#8221; _builder_version=&#8221;4.4.4&#8243; background_color=&#8221;#00aced&#8221; follow_button=&#8221;off&#8221; url_new_window=&#8221;on&#8221;]twitter[\/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network=&#8221;facebook&#8221; url=&#8221;https:\/\/www.facebook.com\/bergCOMPUTERS&#8221; _builder_version=&#8221;4.4.4&#8243; background_color=&#8221;#3b5998&#8243; follow_button=&#8221;off&#8221; url_new_window=&#8221;on&#8221;]facebook[\/et_pb_social_media_follow_network][et_pb_social_media_follow_network social_network=&#8221;instagram&#8221; url=&#8221;https:\/\/www.instagram.com\/berg_software\/&#8221; _builder_version=&#8221;4.4.4&#8243; background_color=&#8221;#ea2c59&#8243; follow_button=&#8221;off&#8221; url_new_window=&#8221;on&#8221;]instagram[\/et_pb_social_media_follow_network][\/et_pb_social_media_follow][\/et_pb_column][et_pb_column type=&#8221;1_3&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_post_nav in_same_term=&#8221;on&#8221; show_next=&#8221;off&#8221; _builder_version=&#8221;4.4.1&#8243; title_text_color=&#8221;#ff6317&#8243; custom_padding=&#8221;|25px|||false|false&#8221;][\/et_pb_post_nav][\/et_pb_column][et_pb_column type=&#8221;1_3&#8243; _builder_version=&#8221;4.4.1&#8243;][et_pb_post_nav in_same_term=&#8221;on&#8221; show_prev=&#8221;off&#8221; _builder_version=&#8221;4.4.1&#8243; title_text_color=&#8221;#ff6317&#8243; custom_padding=&#8221;|||25px|false|false&#8221;][\/et_pb_post_nav][\/et_pb_column][\/et_pb_row][\/et_pb_section][et_pb_section fb_built=&#8221;1&#8243; admin_label=&#8221;CONTACT&#8221; _builder_version=&#8221;4.4.1&#8243; background_color=&#8221;#d2d2d2&#8243; custom_padding=&#8221;75px||75px||false|false&#8221;][et_pb_row column_structure=&#8221;1_4,3_4&#8243; admin_label=&#8221;Service Section Title&#8221; _builder_version=&#8221;4.4.1&#8243; custom_padding=&#8221;||25px||false|false&#8221; animation_direction=&#8221;top&#8221; locked=&#8221;off&#8221;][et_pb_column type=&#8221;1_4&#8243; _builder_version=&#8221;3.25&#8243; custom_padding=&#8221;|||&#8221; custom_padding__hover=&#8221;|||&#8221;][\/et_pb_column][et_pb_column type=&#8221;3_4&#8243; _builder_version=&#8221;3.25&#8243; custom_padding=&#8221;|||&#8221; custom_padding__hover=&#8221;|||&#8221;][et_pb_text _builder_version=&#8221;4.4.1&#8243; text_font=&#8221;|300|||||||&#8221; text_text_color=&#8221;#ffffff&#8221; text_line_height=&#8221;1.1em&#8221; header_5_font=&#8221;|600|||||||&#8221; header_5_text_color=&#8221;#ffffff&#8221; header_5_font_size=&#8221;14px&#8221; header_5_line_height=&#8221;1.5em&#8221; custom_margin=&#8221;||||false|false&#8221;]29 years in business | 2700 software projects | 760 clients | 24 countries<\/p>\n<h5>We turn ideas into software. What is yours?<\/h5>\n<p>[\/et_pb_text][et_pb_text admin_label=&#8221;Title&#8221; _builder_version=&#8221;4.4.6&#8243; header_text_align=&#8221;center&#8221; header_2_font=&#8221;|300|||||||&#8221; header_2_text_align=&#8221;left&#8221; header_2_text_color=&#8221;#ff6317&#8243; header_2_font_size=&#8221;50px&#8221; header_2_line_height=&#8221;0.9em&#8221; custom_margin=&#8221;||||false|false&#8221; custom_padding=&#8221;25px||25px||false|false&#8221;]<\/p>\n<h2 id=\"Getintouch\">Get in touch<\/h2>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][et_pb_row column_structure=&#8221;1_4,3_4&#8243; _builder_version=&#8221;4.4.4&#8243;][et_pb_column type=&#8221;1_4&#8243; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_column][et_pb_column type=&#8221;3_4&#8243; _builder_version=&#8221;4.4.4&#8243;][et_pb_contact_form email=&#8221;contact@bergsoftprod.wpengine.com&#8221; custom_message=&#8221;WEBFORM MESSAGE||et_pb_line_break_holder||\uff3f||et_pb_line_break_holder||FROM: %%Name%%||et_pb_line_break_holder||EMAIL: %%Email%%||et_pb_line_break_holder||PHONE NUMBER: %%Phone_number%%||et_pb_line_break_holder||COMPANY: %%Company_name%%||et_pb_line_break_holder||TERMS &#038; CONDITIONS: %%Terms_and_Conditions%%||et_pb_line_break_holder||DATA PRIVACY POLICY: %%Data_Privacy_Policy%%||et_pb_line_break_holder||\uff3f||et_pb_line_break_holder||MESSAGE:||et_pb_line_break_holder||%%Message%%&#8221; success_message=&#8221;Thank you for reaching out! Your message was sent. We will get back to you right away.&#8221; _builder_version=&#8221;4.4.4&#8243; form_field_background_color=&#8221;#d2d2d2&#8243; form_field_text_color=&#8221;#ffffff&#8221; form_field_focus_background_color=&#8221;#ffffff&#8221; form_field_focus_text_color=&#8221;#000000&#8243; title_level=&#8221;h2&#8243; title_font=&#8221;|300|||||||&#8221; title_text_color=&#8221;#ff6317&#8243; title_font_size=&#8221;30px&#8221; form_field_line_height=&#8221;1.5em&#8221; custom_button=&#8221;on&#8221; button_text_size=&#8221;14px&#8221; button_text_color=&#8221;#ffffff&#8221; button_bg_color=&#8221;#ff6317&#8243; button_border_width=&#8221;0px&#8221; button_border_radius=&#8221;0px&#8221; button_icon=&#8221;%%3%%&#8221; button_on_hover=&#8221;off&#8221; border_color_all=&#8221;#ffffff&#8221; border_width_bottom=&#8221;1px&#8221;][et_pb_contact_field field_id=&#8221;Name&#8221; field_title=&#8221;Name *&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243; form_field_background_color=&#8221;#d2d2d2&#8243; form_field_text_color=&#8221;#ffffff&#8221; form_field_focus_background_color=&#8221;#ffffff&#8221; form_field_focus_text_color=&#8221;#000000&#8243; border_width_bottom=&#8221;1px&#8221; button_text_size__hover_enabled=&#8221;off&#8221; button_one_text_size__hover_enabled=&#8221;off&#8221; button_two_text_size__hover_enabled=&#8221;off&#8221; button_text_color__hover_enabled=&#8221;off&#8221; button_one_text_color__hover_enabled=&#8221;off&#8221; button_two_text_color__hover_enabled=&#8221;off&#8221; button_border_width__hover_enabled=&#8221;off&#8221; button_one_border_width__hover_enabled=&#8221;off&#8221; button_two_border_width__hover_enabled=&#8221;off&#8221; button_border_color__hover_enabled=&#8221;off&#8221; button_one_border_color__hover_enabled=&#8221;off&#8221; button_two_border_color__hover_enabled=&#8221;off&#8221; button_border_radius__hover_enabled=&#8221;off&#8221; button_one_border_radius__hover_enabled=&#8221;off&#8221; button_two_border_radius__hover_enabled=&#8221;off&#8221; button_letter_spacing__hover_enabled=&#8221;off&#8221; button_one_letter_spacing__hover_enabled=&#8221;off&#8221; button_two_letter_spacing__hover_enabled=&#8221;off&#8221; button_bg_color__hover_enabled=&#8221;off&#8221; button_one_bg_color__hover_enabled=&#8221;off&#8221; button_two_bg_color__hover_enabled=&#8221;off&#8221;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Email&#8221; field_title=&#8221;Email address *&#8221; field_type=&#8221;email&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243; button_text_size__hover_enabled=&#8221;off&#8221; button_one_text_size__hover_enabled=&#8221;off&#8221; button_two_text_size__hover_enabled=&#8221;off&#8221; button_text_color__hover_enabled=&#8221;off&#8221; button_one_text_color__hover_enabled=&#8221;off&#8221; button_two_text_color__hover_enabled=&#8221;off&#8221; button_border_width__hover_enabled=&#8221;off&#8221; button_one_border_width__hover_enabled=&#8221;off&#8221; button_two_border_width__hover_enabled=&#8221;off&#8221; button_border_color__hover_enabled=&#8221;off&#8221; button_one_border_color__hover_enabled=&#8221;off&#8221; button_two_border_color__hover_enabled=&#8221;off&#8221; button_border_radius__hover_enabled=&#8221;off&#8221; button_one_border_radius__hover_enabled=&#8221;off&#8221; button_two_border_radius__hover_enabled=&#8221;off&#8221; button_letter_spacing__hover_enabled=&#8221;off&#8221; button_one_letter_spacing__hover_enabled=&#8221;off&#8221; button_two_letter_spacing__hover_enabled=&#8221;off&#8221; button_bg_color__hover_enabled=&#8221;off&#8221; button_one_bg_color__hover_enabled=&#8221;off&#8221; button_two_bg_color__hover_enabled=&#8221;off&#8221;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Phone_number&#8221; field_title=&#8221;Phone number&#8221; required_mark=&#8221;off&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Company_name&#8221; field_title=&#8221;Company name&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Message&#8221; field_title=&#8221;Message *&#8221; field_type=&#8221;text&#8221; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243; form_field_background_color=&#8221;#d2d2d2&#8243; form_field_focus_background_color=&#8221;#ffffff&#8221; form_field_focus_text_color=&#8221;#000000&#8243; border_width_bottom=&#8221;1px&#8221; button_text_size__hover_enabled=&#8221;off&#8221; button_one_text_size__hover_enabled=&#8221;off&#8221; button_two_text_size__hover_enabled=&#8221;off&#8221; button_text_color__hover_enabled=&#8221;off&#8221; button_one_text_color__hover_enabled=&#8221;off&#8221; button_two_text_color__hover_enabled=&#8221;off&#8221; button_border_width__hover_enabled=&#8221;off&#8221; button_one_border_width__hover_enabled=&#8221;off&#8221; button_two_border_width__hover_enabled=&#8221;off&#8221; button_border_color__hover_enabled=&#8221;off&#8221; button_one_border_color__hover_enabled=&#8221;off&#8221; button_two_border_color__hover_enabled=&#8221;off&#8221; button_border_radius__hover_enabled=&#8221;off&#8221; button_one_border_radius__hover_enabled=&#8221;off&#8221; button_two_border_radius__hover_enabled=&#8221;off&#8221; button_letter_spacing__hover_enabled=&#8221;off&#8221; button_one_letter_spacing__hover_enabled=&#8221;off&#8221; button_two_letter_spacing__hover_enabled=&#8221;off&#8221; button_bg_color__hover_enabled=&#8221;off&#8221; button_one_bg_color__hover_enabled=&#8221;off&#8221; button_two_bg_color__hover_enabled=&#8221;off&#8221;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Terms_and_Conditions&#8221; field_title=&#8221; &#8221; field_type=&#8221;checkbox&#8221; checkbox_options=&#8221;%91{%22value%22:%22I have read and accepted the Terms and Conditions%22,%22checked%22:0,%22dragID%22:-1}%93&#8243; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_contact_field][et_pb_contact_field field_id=&#8221;Data_Privacy_Policy&#8221; field_title=&#8221; &#8221; field_type=&#8221;checkbox&#8221; checkbox_options=&#8221;%91{%22value%22:%22I have read and accepted the Data Privacy Policy%22,%22checked%22:0,%22dragID%22:-1}%93&#8243; fullwidth_field=&#8221;on&#8221; _builder_version=&#8221;4.4.4&#8243;][\/et_pb_contact_field][\/et_pb_contact_form][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>End-to-end tests are a safety net to increase software quality. We discuss development, maintenance, and Gitlab integration.<\/p>\n","protected":false},"author":12,"featured_media":6306,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"","_et_gb_content_width":"2880","footnotes":""},"categories":[33],"tags":[],"class_list":["post-6213","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-insights"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v16.1.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>End-to-end tests &amp; GitLab integration | Berg Software<\/title>\n<meta name=\"description\" content=\"End-to-end tests are a safety net to increase software quality. We discuss development, maintenance, and Gitlab integration.\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"End-to-end tests &amp; GitLab integration | Berg Software\" \/>\n<meta property=\"og:description\" content=\"End-to-end tests are a safety net to increase software quality. We discuss development, maintenance, and Gitlab integration.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/\" \/>\n<meta property=\"og:site_name\" content=\"Berg Software\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/bergCOMPUTERS\/\" \/>\n<meta property=\"article:published_time\" content=\"2020-10-20T17:50:40+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-09-16T13:38:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-00-Cover.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"600\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@berg_software\" \/>\n<meta name=\"twitter:site\" content=\"@berg_software\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\">\n\t<meta name=\"twitter:data1\" content=\"39 minutes\">\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.berg-software.com\/en\/#organization\",\"name\":\"Berg Software\",\"url\":\"https:\/\/www.berg-software.com\/en\/\",\"sameAs\":[\"https:\/\/www.facebook.com\/bergCOMPUTERS\/\",\"https:\/\/www.instagram.com\/berg_software\/\",\"https:\/\/www.linkedin.com\/company\/berg-computers-srl\/\",\"https:\/\/www.youtube.com\/channel\/UCw1FfcRJnC-CoKPwlcM10Iw\",\"https:\/\/twitter.com\/berg_software\"],\"logo\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/www.berg-software.com\/en\/#logo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/berg-software.com\/wp-content\/uploads\/berg-software-logo.png\",\"contentUrl\":\"https:\/\/berg-software.com\/wp-content\/uploads\/berg-software-logo.png\",\"width\":512,\"height\":512,\"caption\":\"Berg Software\"},\"image\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#logo\"}},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.berg-software.com\/en\/#website\",\"url\":\"https:\/\/www.berg-software.com\/en\/\",\"name\":\"Berg Software\",\"description\":\"We turn ideas into software.\",\"publisher\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/www.berg-software.com\/en\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/www.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-00-Cover.jpg\",\"contentUrl\":\"https:\/\/www.berg-software.com\/wp-content\/uploads\/Berg-Software-End-to-end-tests-an-GitLab-integration-00-Cover.jpg\",\"width\":1200,\"height\":600,\"caption\":\"Berg Software - End-to-end tests an GitLab integration - 00 Cover\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/#webpage\",\"url\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/\",\"name\":\"End-to-end tests & GitLab integration | Berg Software\",\"isPartOf\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/#primaryimage\"},\"datePublished\":\"2020-10-20T17:50:40+00:00\",\"dateModified\":\"2022-09-16T13:38:10+00:00\",\"description\":\"End-to-end tests are a safety net to increase software quality. We discuss development, maintenance, and Gitlab integration.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.berg-software.com\/en\/\",\"url\":\"https:\/\/www.berg-software.com\/en\/\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"position\":2,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.berg-software.com\/en\/category\/insights\/\",\"url\":\"https:\/\/www.berg-software.com\/en\/category\/insights\/\",\"name\":\"Insights\"}},{\"@type\":\"ListItem\",\"position\":3,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/\",\"url\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/\",\"name\":\"End-to-end tests journey &#038; Integration into a GitLab pipeline\"}}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/#webpage\"},\"author\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#\/schema\/person\/4becfb1c9de08963757e6dc8531fef1a\"},\"headline\":\"End-to-end tests journey &#038; Integration into a GitLab pipeline\",\"datePublished\":\"2020-10-20T17:50:40+00:00\",\"dateModified\":\"2022-09-16T13:38:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/#webpage\"},\"publisher\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.berg-software.com\/en\/end-to-end-tests-and-gitlab-integration\/#primaryimage\"},\"articleSection\":\"Insights\",\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.berg-software.com\/en\/#\/schema\/person\/4becfb1c9de08963757e6dc8531fef1a\",\"name\":\"Ionu\\u021b Bucurescu\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","_links":{"self":[{"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/posts\/6213","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/users\/12"}],"replies":[{"embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/comments?post=6213"}],"version-history":[{"count":0,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/posts\/6213\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/media\/6306"}],"wp:attachment":[{"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/media?parent=6213"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/categories?post=6213"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.berg-software.com\/en\/wp-json\/wp\/v2\/tags?post=6213"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}