diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 50074737b..5d7736204 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -150,9 +150,20 @@ build-ci-util-image:
     - cd .gitlab/ci_util_image
     - docker build -t $CI_UTIL_IMAGE .
     - docker push $CI_UTIL_IMAGE
+  # Only trigger this job manually to prevent it running every single time a new branch
+  # is made. See https://gitlab.com/gitlab-org/gitlab/-/issues/11427
+  when:
+    manual
+  # We can't use the newer rules: syntax due to https://gitlab.com/gitlab-org/gitlab/-/issues/34756
   only:
     changes:
       - .gitlab/ci_util_image/*
+  except:
+    refs:
+      # When a pipeline is triggered by an upstream project we don't want to rebuild.
+      - pipelines
+      # When a pipeline is triggered by a git commit tag we don't want to rebuild.
+      - tags
 
 # ==================================== BACKEND ====================================
 
diff --git a/.gitlab/ci_includes/jobs.yml b/.gitlab/ci_includes/jobs.yml
index 3ef0f4c46..389ceb2fb 100644
--- a/.gitlab/ci_includes/jobs.yml
+++ b/.gitlab/ci_includes/jobs.yml
@@ -9,12 +9,13 @@
   image: docker:20.10.12
   stage: build
   interruptible: true
-  # Prevent rebuilds when tagging as all we want to do is tag and push the already built image
+  # We can't use the newer rules: syntax due to https://gitlab.com/gitlab-org/gitlab/-/issues/34756
   except:
     refs:
+      # When a pipeline is triggered by an upstream project we don't want to rebuild.
       - pipelines
-    variables:
-      - $CI_COMMIT_TAG
+      # When a pipeline is triggered by a git commit tag we don't want to rebuild.
+      - tags
   services:
     - docker:20.10.12-dind
   variables:
@@ -124,12 +125,13 @@
   image: $CI_UTIL_IMAGE
   stage: build-final
   interruptible: true
-  # Prevent rebuilds when tagging as all we want to do is tag and push
+  # We can't use the newer rules: syntax due to https://gitlab.com/gitlab-org/gitlab/-/issues/34756
   except:
     refs:
+      # When a pipeline is triggered by an upstream project we don't want to rebuild.
       - pipelines
-    variables:
-      - $CI_COMMIT_TAG
+      # When a pipeline is triggered by a git commit tag we don't want to rebuild.
+      - tags
   services:
     - docker:20.10.12-dind
   variables:
@@ -252,12 +254,13 @@
   stage: test
   image: $CI_UTIL_IMAGE
   interruptible: true
-  # Prevent rebuilds when tagging as all we want to do is tag and push
+  # We can't use the newer rules: syntax due to https://gitlab.com/gitlab-org/gitlab/-/issues/34756
   except:
     refs:
+      # When a pipeline is triggered by an upstream project we don't want to retest.
       - pipelines
-    variables:
-      - $CI_COMMIT_TAG
+      # When a pipeline is triggered by a git commit tag we don't want to retest.
+      - tags
   services:
     - docker:20.10.12-dind
 
diff --git a/web-frontend/Makefile b/web-frontend/Makefile
index 1c60d7a46..b53501d32 100644
--- a/web-frontend/Makefile
+++ b/web-frontend/Makefile
@@ -20,7 +20,7 @@ unit-test:
 	npx jest --selectProjects unit --selectProjects premium || exit;
 
 ci-test-javascript:
-	npx jest -i --ci --verbose || exit;
+	npx jest -i --ci --forceExit --verbose || exit;
 
 unit-test-watch:
 	npx jest test/unit --watch || exit;
diff --git a/web-frontend/test/helpers/create-nuxt.js b/web-frontend/test/helpers/create-nuxt.js
index 9b2fca468..b39aeed7f 100644
--- a/web-frontend/test/helpers/create-nuxt.js
+++ b/web-frontend/test/helpers/create-nuxt.js
@@ -1,8 +1,8 @@
 import { Builder, Nuxt } from 'nuxt'
 
-import config from '@/config/nuxt.config.test'
+import config from '@baserow/config/nuxt.config.test'
 
-export default async function createNuxt(buildAndListen = false, port = 3001) {
+export default async function createNuxt(buildAndListen = false, port = 3501) {
   const nuxt = new Nuxt(config)
   await nuxt.ready()
   if (buildAndListen) {
diff --git a/web-frontend/test/server/core/pages/login.spec.js b/web-frontend/test/server/core/pages/login.spec.js
deleted file mode 100644
index 3cc25fa26..000000000
--- a/web-frontend/test/server/core/pages/login.spec.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import createNuxt from '@/test/helpers/create-nuxt'
-
-let nuxt = null
-
-describe('children', () => {
-  beforeAll(async (done) => {
-    nuxt = await createNuxt(true, 3502)
-    done()
-  }, 120000)
-
-  test('/login', async () => {
-    const { html } = await nuxt.server.renderRoute('/login')
-    expect(html).toContain('Login')
-  })
-
-  afterAll(async () => {
-    await nuxt.close()
-  })
-})
diff --git a/web-frontend/test/server/core/pages/index.spec.js b/web-frontend/test/server/core/pages/server.spec.js
similarity index 79%
rename from web-frontend/test/server/core/pages/index.spec.js
rename to web-frontend/test/server/core/pages/server.spec.js
index 730356e73..f452820d0 100644
--- a/web-frontend/test/server/core/pages/index.spec.js
+++ b/web-frontend/test/server/core/pages/server.spec.js
@@ -2,7 +2,7 @@ import axios from 'axios'
 import MockAdapter from 'axios-mock-adapter'
 import httpMocks from 'node-mocks-http'
 
-import createNuxt from '@/test/helpers/create-nuxt'
+import createNuxt from '@baserow/test/helpers/create-nuxt'
 
 let nuxt = null
 let mock = null
@@ -26,7 +26,7 @@ describe('index redirect', () => {
       },
     })
 
-    nuxt = await createNuxt(true, 3501)
+    nuxt = await createNuxt(true)
     done()
   })
 
@@ -48,7 +48,13 @@ describe('index redirect', () => {
     expect(redirected.status).toBe(302)
   })
 
-  afterAll(async () => {
-    await nuxt.close()
+  test('login page renders', async () => {
+    const { html } = await nuxt.server.renderRoute('/login')
+    expect(html).toContain('Login')
+  })
+
+  test('sign up page renders', async () => {
+    const { html } = await nuxt.server.renderRoute('/signup')
+    expect(html).toContain('Sign up')
   })
 })
diff --git a/web-frontend/test/server/core/pages/signup.spec.js b/web-frontend/test/server/core/pages/signup.spec.js
deleted file mode 100644
index b0ec74ee9..000000000
--- a/web-frontend/test/server/core/pages/signup.spec.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import createNuxt from '@/test/helpers/create-nuxt'
-
-let nuxt = null
-
-describe('children', () => {
-  beforeAll(async (done) => {
-    nuxt = await createNuxt(true, 3503)
-    done()
-  }, 120000)
-
-  test('/login', async () => {
-    const { html } = await nuxt.server.renderRoute('/signup')
-    expect(html).toContain('Sign up')
-  })
-
-  afterAll(async () => {
-    await nuxt.close()
-  })
-})
diff --git a/web-frontend/yarn.lock b/web-frontend/yarn.lock
index ff8a3cd71..eaf2347e4 100644
--- a/web-frontend/yarn.lock
+++ b/web-frontend/yarn.lock
@@ -2423,6 +2423,11 @@ ansi-regex@^5.0.0:
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
   integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
 
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
 ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@@ -6925,6 +6930,16 @@ jest-jasmine2@^26.6.3:
     pretty-format "^26.6.2"
     throat "^5.0.0"
 
+jest-junit@^13.0.0:
+  version "13.0.0"
+  resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-13.0.0.tgz#479be347457aad98ae8a5983a23d7c3ec526c9a3"
+  integrity sha512-JSHR+Dhb32FGJaiKkqsB7AR3OqWKtldLd6ZH2+FJ8D4tsweb8Id8zEVReU4+OlrRO1ZluqJLQEETm+Q6/KilBg==
+  dependencies:
+    mkdirp "^1.0.4"
+    strip-ansi "^6.0.1"
+    uuid "^8.3.2"
+    xml "^1.0.1"
+
 jest-leak-detector@^26.6.2:
   version "26.6.2"
   resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af"
@@ -11117,6 +11132,13 @@ strip-ansi@^6.0.0:
   dependencies:
     ansi-regex "^5.0.0"
 
+strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
 strip-bom@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@@ -12033,7 +12055,7 @@ uuid@^3.3.2:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
   integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
 
-uuid@^8.3.0:
+uuid@^8.3.0, uuid@^8.3.2:
   version "8.3.2"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
   integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
@@ -12534,6 +12556,11 @@ xml-name-validator@^3.0.0:
   resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
   integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
 
+xml@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
+  integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=
+
 xmlchars@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"