diff --git a/qa/qa/ee.rb b/qa/qa/ee.rb
index e6d0c1040a786b256e0f76de86bc5c23036c4e26..94b73753902c3c7a0b100502838558c7994f6077 100644
--- a/qa/qa/ee.rb
+++ b/qa/qa/ee.rb
@@ -153,6 +153,7 @@ module Metrics
 
         module Pipeline
           autoload :Show, 'qa/ee/page/project/pipeline/show'
+          autoload :Index, 'qa/ee/page/project/pipeline/index'
         end
 
         module Secure
@@ -174,6 +175,10 @@ module Packages
         module Snippet
           autoload :Index, 'qa/ee/page/project/snippet/index'
         end
+
+        module Job
+          autoload :Show, 'qa/ee/page/project/job/show'
+        end
       end
 
       module MergeRequest
diff --git a/qa/qa/ee/page/project/job/show.rb b/qa/qa/ee/page/project/job/show.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0613dbe2ceb8dfed423b1e4d40e881a70a2eaa8b
--- /dev/null
+++ b/qa/qa/ee/page/project/job/show.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module QA
+  module EE
+    module Page
+      module Project
+        module Job
+          module Show
+            extend QA::Page::PageConcern
+
+            # These wait times cover both the time for pipeline job to complete and time for log and artifact to Geo replicate, so the max duration has been doubled
+            def wait_for_job_log_replication
+              QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_job_log_replication])
+              wait_until(max_duration: 2 * Runtime::Geo.max_file_replication_time) do
+                has_job_log?
+              end
+            end
+
+            def wait_for_job_artifact_replication
+              QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_job_artifact_replication])
+              wait_until(max_duration: 2 * Runtime::Geo.max_file_replication_time) do
+                has_browse_button?
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/qa/qa/ee/page/project/pipeline/index.rb b/qa/qa/ee/page/project/pipeline/index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3c488900f1d98c3d18a8218ec699a40496383c55
--- /dev/null
+++ b/qa/qa/ee/page/project/pipeline/index.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module QA
+  module EE
+    module Page
+      module Project
+        module Pipeline
+          module Index
+            extend QA::Page::PageConcern
+
+            def wait_for_latest_pipeline_replication
+              QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_latest_pipeline_replication])
+              wait_until(max_duration: Runtime::Geo.max_file_replication_time) do
+                within_element_by_index(:pipeline_commit_status, 0) { has_text?('passed') || has_text?('failed') }
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/qa/qa/ee/page/project/pipeline/show.rb b/qa/qa/ee/page/project/pipeline/show.rb
index 09c4ce50b674f9538bda42803bbda9336029145e..b75c2a7c8b732285f4de526527f8d57cc1f27aa1 100644
--- a/qa/qa/ee/page/project/pipeline/show.rb
+++ b/qa/qa/ee/page/project/pipeline/show.rb
@@ -34,6 +34,13 @@ def click_on_licenses
             def has_license_count_of?(count)
               find_element(:licenses_counter).has_content?(count)
             end
+
+            def wait_for_pipeline_job_replication(name)
+              QA::Runtime::Logger.debug(%Q[#{self.class.name} - wait_for_pipeline_job_replication])
+              wait_until(max_duration: Runtime::Geo.max_file_replication_time) do
+                has_job?(name)
+              end
+            end
           end
         end
       end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index 6a657b4ab390682ea82de945f4bd8ccf43a753e5..2ecb27e05b2ccf93860b45601817a5445745777e 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -58,6 +58,10 @@ def retry!
             click_element :retry_button
           end
 
+          def has_job_log?
+            has_element? :job_log_content
+          end
+
           private
 
           def loaded?(wait: 60)
@@ -70,3 +74,5 @@ def loaded?(wait: 60)
     end
   end
 end
+
+QA::Page::Project::Job::Show.prepend_if_ee('QA::EE::Page::Project::Job::Show')
diff --git a/qa/qa/page/project/pipeline/index.rb b/qa/qa/page/project/pipeline/index.rb
index b2ecbb2e98812ef2707d159678ea22615658584f..aff2378330ab57c6af81b48c5649b959843efe12 100644
--- a/qa/qa/page/project/pipeline/index.rb
+++ b/qa/qa/page/project/pipeline/index.rb
@@ -57,3 +57,5 @@ def click_run_pipeline_button
     end
   end
 end
+
+QA::Page::Project::Pipeline::Index.prepend_if_ee('QA::EE::Page::Project::Pipeline::Index')
diff --git a/qa/qa/specs/features/ee/browser_ui/geo/geo_replication_ci_job_log_artifacts_spec.rb b/qa/qa/specs/features/ee/browser_ui/geo/geo_replication_ci_job_log_artifacts_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c8931ea6aa3d6c2cd70db38346bde6c0fb1a593a
--- /dev/null
+++ b/qa/qa/specs/features/ee/browser_ui/geo/geo_replication_ci_job_log_artifacts_spec.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+module QA
+  RSpec.describe 'Geo', :orchestrated, :runner, :requires_admin, :geo do
+    describe 'CI job' do
+      before(:all) do
+        @file_name = 'geo_artifact.txt'
+        @directory_name = 'geo_artifacts'
+        @pipeline_job_name = 'test-artifacts'
+        executor = "qa-runner-#{Time.now.to_i}"
+
+        @project = Resource::Project.fabricate_via_api! do |project|
+          project.name = 'geo-project-with-artifacts'
+        end
+
+        @runner = Resource::Runner.fabricate! do |runner|
+          runner.project = @project
+          runner.name = executor
+          runner.tags = [executor]
+        end
+
+        Resource::Repository::Commit.fabricate_via_api! do |commit|
+          commit.project = @project
+          commit.commit_message = 'Add .gitlab-ci.yml'
+          commit.add_files(
+            [
+              {
+                file_path: '.gitlab-ci.yml',
+                content: <<~YAML
+                  test-artifacts:
+                    tags:
+                      - '#{executor}'
+                    artifacts:
+                      paths:
+                        - '#{@directory_name}'
+                      expire_in: 1000 seconds
+                    script:
+                      - |
+                        mkdir #{@directory_name}
+                        echo "CONTENTS" > #{@directory_name}/#{@file_name}
+                YAML
+              }
+            ]
+          )
+        end
+      end
+
+      after(:all) do
+        @runner.remove_via_api!
+      end
+
+      # Test code is based on qa/specs/features/ee/browser_ui/4_verify/locked_artifacts_spec.rb
+      it 'replicates the job log to the secondary Geo site', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/908' do
+        Runtime::Logger.debug('Visiting the secondary Geo site')
+
+        Flow::Login.while_signed_in(address: :geo_secondary) do
+          EE::Page::Main::Banner.perform do |banner|
+            expect(banner).to have_secondary_read_only_banner
+          end
+
+          Page::Main::Menu.perform(&:go_to_projects)
+
+          Page::Dashboard::Projects.perform do |dashboard|
+            dashboard.wait_for_project_replication(@project.name)
+            dashboard.go_to_project(@project.name)
+          end
+
+          Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+          Page::Project::Pipeline::Index.perform do |index|
+            index.wait_for_latest_pipeline_replication
+            index.click_on_latest_pipeline
+          end
+
+          Page::Project::Pipeline::Show.perform do |pipeline|
+            pipeline.wait_for_pipeline_job_replication(@pipeline_job_name)
+            pipeline.click_job(@pipeline_job_name)
+          end
+
+          Page::Project::Job::Show.perform do |pipeline_job|
+            pipeline_job.wait_for_job_log_replication
+            expect(pipeline_job).to have_job_log
+          end
+        end
+      end
+
+      it 'replicates the job artifact to the secondary Geo site', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/186' do
+        Runtime::Logger.debug('Visiting the secondary Geo site')
+
+        Flow::Login.while_signed_in(address: :geo_secondary) do
+          EE::Page::Main::Banner.perform do |banner|
+            expect(banner).to have_secondary_read_only_banner
+          end
+
+          Page::Main::Menu.perform(&:go_to_projects)
+
+          Page::Dashboard::Projects.perform do |dashboard|
+            dashboard.wait_for_project_replication(@project.name)
+            dashboard.go_to_project(@project.name)
+          end
+
+          Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+          Page::Project::Pipeline::Index.perform do |index|
+            index.wait_for_latest_pipeline_replication
+            index.click_on_latest_pipeline
+          end
+
+          Page::Project::Pipeline::Show.perform do |pipeline|
+            pipeline.wait_for_pipeline_job_replication(@pipeline_job_name)
+            pipeline.click_job(@pipeline_job_name)
+          end
+
+          Page::Project::Job::Show.perform do |pipeline_job|
+            pipeline_job.wait_for_job_artifact_replication
+            pipeline_job.click_browse_button
+          end
+
+          EE::Page::Project::Artifact::Show.perform do |artifact|
+            artifact.go_to_directory(@directory_name)
+            expect(artifact).to have_content(@file_name)
+          end
+        end
+      end
+    end
+  end
+end