diff --git a/build.gradle b/build.gradle
index 2d1c437e08d0ada1919e969a094306fa33877819..6157c5444e350c8d18585c8b4068263432e0ba5a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,6 +2,7 @@ plugins {
 	id 'java'
 	id 'org.springframework.boot' version '3.3.5'
 	id 'io.spring.dependency-management' version '1.1.6'
+	id 'jacoco' // 引入 JaCoCo 插件
 }
 
 group = 'uk.ac.cf.spring'
@@ -28,20 +29,27 @@ dependencies {
 	implementation 'org.springframework.boot:spring-boot-starter-web'
 	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
 	//from https://mariadb.com/kb/en/java-connector-using-gradle/
+	implementation 'junit:junit:4.12'
 
 	implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.3'
 	// implementation 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
 	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
 	implementation 'org.mariadb.jdbc:mariadb-java-client'
-	
+
 	implementation 'org.springframework.boot:spring-boot-starter-validation'
-    implementation 'org.springframework.boot:spring-boot-starter-security'
+	implementation 'org.springframework.boot:spring-boot-starter-security'
 
-    compileOnly 'org.projectlombok:lombok'
+	compileOnly 'org.projectlombok:lombok'
 	developmentOnly 'org.springframework.boot:spring-boot-devtools'
 	annotationProcessor 'org.projectlombok:lombok'
 	testImplementation 'org.springframework.boot:spring-boot-starter-test'
 	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+	testImplementation 'org.springframework.security:spring-security-test'
+//	testImplementation("io.github.bonigarcia:webdrivermanager:5.2.0")
+//	testImplementation group: 'net.sourceforge.htmlunit', name: 'htmlunit', version: '2.32'
+//	testImplementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '4.1.0'
+
+
 }
 
 tasks.named('test') {
diff --git a/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java
index 78375adfecd8616f7f27307ecd807ecee584005b..16616a16959c078418945d000f9d428cef6e92bd 100644
--- a/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java
+++ b/src/main/java/uk/ac/cf/spring/demo/sports/security/SecurityConfig.java
@@ -26,6 +26,7 @@ public class SecurityConfig {
             "/api/**",          // API
             "/html/**",         // static HTML
             "/html/register.html",        // register
+            "/register",
             "/html/login.html",  // login
             "/html/table-tennisrules.html",
             "/html/login.html" , // login
@@ -46,9 +47,7 @@ public class SecurityConfig {
                         .anyRequest().authenticated() // 其他页面需要认证
                 )
                 .csrf().disable()
-//                .formLogin(form -> form
-//                        .loginPage("/login").permitAll() // 定义自定义登录页面
-//                )
+
                 .formLogin(form -> form
                         .loginPage("/html/login.html").permitAll() // 使用自定义页面
                         .defaultSuccessUrl("/html/matchSchedule.html", true) // 登录成功后跳转
diff --git a/src/main/java/uk/ac/cf/spring/demo/user/controller/RegistrationController.java b/src/main/java/uk/ac/cf/spring/demo/user/controller/RegistrationController.java
index fa495dad7d9541090db6c7e664ef5a9c2504e9fd..93be8506b0b4acc70f0c0c814458412b88f04116 100644
--- a/src/main/java/uk/ac/cf/spring/demo/user/controller/RegistrationController.java
+++ b/src/main/java/uk/ac/cf/spring/demo/user/controller/RegistrationController.java
@@ -18,7 +18,7 @@ public class RegistrationController {
 
     @GetMapping("/register")
     public String showRegistrationForm() {
-        return "page/register"; // return register.html
+        return "html/register.html"; // return register.html
     }
 
 
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
index 1aae39ac109a3886b36e7f09f800e1518b886e8a..9fb6f3641a07554d8222ed915040609447bdecac 100644
--- a/src/main/resources/schema.sql
+++ b/src/main/resources/schema.sql
@@ -1,3 +1,5 @@
+CREATE DATABASE IF NOT EXISTS sports;
+USE sports;
 drop table if exists match_item;
 drop table if exists ranking;
 drop table if exists information;
diff --git a/src/main/resources/sports.sql b/src/main/resources/sports.sql
new file mode 100644
index 0000000000000000000000000000000000000000..eb401e775ab0876f563854fec266c6e8b4856ce2
--- /dev/null
+++ b/src/main/resources/sports.sql
@@ -0,0 +1,98 @@
+CREATE DATABASE IF NOT EXISTS sports;
+USE sports;
+drop table if exists match_item;
+drop table if exists ranking;
+drop table if exists information;
+drop table if exists images;
+CREATE TABLE information (
+                             id INT AUTO_INCREMENT PRIMARY KEY,
+                             username VARCHAR(50) UNIQUE NOT NULL,
+                             email VARCHAR(50) UNIQUE NOT NULL,
+                             password VARCHAR(255) NOT NULL,
+                             role VARCHAR(20) DEFAULT 'USER' NOT NULL
+);
+create table if not exists match_item (
+    -- Match ID
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    -- Sport type
+    sport ENUM('Pools', 'Darts', 'TableTennis') NOT NULL,
+    -- Player A ID
+    player_AId INT NULL,
+    -- Player B ID
+    player_BId INT NULL,
+    plan_Time DATETIME NOT NULL,
+    -- Match status
+    status ENUM('pending', 'confirmed', 'completed') DEFAULT 'pending',
+    score_a INT DEFAULT 0,
+    score_b INT DEFAULT 0,
+    confirm_a BOOLEAN DEFAULT FALSE,
+    confirm_b BOOLEAN DEFAULT FALSE,
+    winner_id BIGINT DEFAULT NULL
+#     FOREIGN KEY (player_AId) REFERENCES information(id) ON DELETE CASCADE,
+#     FOREIGN KEY (player_BId) REFERENCES information(id) ON DELETE CASCADE
+#     FOREIGN KEY (player_AId) REFERENCES information(id) ON DELETE SET NULL,
+#     FOREIGN KEY (player_BId) REFERENCES information(id) ON DELETE SET NULL
+    );
+
+
+CREATE TABLE IF NOT EXISTS ranking (
+                                       id INT AUTO_INCREMENT PRIMARY KEY,
+                                       user_id INT NOT NULL,
+                                       sport ENUM('Pools', 'Darts', 'TableTennis') NOT NULL,
+                                       wins INT DEFAULT 0,
+                                       username VARCHAR(255) DEFAULT NULL,
+                                       UNIQUE (user_id, sport) -- 添加唯一约束
+);
+CREATE TABLE images (
+                        id INT AUTO_INCREMENT PRIMARY KEY,
+                        url VARCHAR(255) NOT NULL,  -- 图片文件名或路径
+                        uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP-- 上传时间
+);
+delete from information;
+delete from match_item;
+delete from ranking;
+INSERT INTO information (id, username, email, password, role)
+VALUES (1, 'shy', 'shy@creditsafe.com', '$2a$16$R0aSGzbklUhpRfIMhocewephgUDMFOffLb7faSwwHusqHh81G026i', 'USER');
+INSERT INTO information (id, username, email, password, role)
+VALUES (2, 'zyb', 'zyb@creditsafe.com', '$2a$16$R0aSGzbklUhpRfIMhocewephgUDMFOffLb7faSwwHusqHh81G026i', 'USER');
+INSERT INTO information (id, username, email, password, role)
+VALUES (3, 'xzc', 'xzc@creditsafe.com', '$2a$16$R0aSGzbklUhpRfIMhocewephgUDMFOffLb7faSwwHusqHh81G026i', 'USER');
+INSERT INTO information (id, username, email, password, role)
+VALUES (4, 'xx', 'xx@creditsafe.com', '$2a$16$R0aSGzbklUhpRfIMhocewephgUDMFOffLb7faSwwHusqHh81G026i', 'USER');
+insert into match_item (sport, player_AId, player_BId, plan_Time, status, score_a, score_b, confirm_a, confirm_b, winner_id)
+values
+--     test data
+-- 1: Pending status
+    ('Pools', 1, 2, '2024-11-25 14:30:00', 'pending', 0, 0, FALSE, FALSE, NULL),
+-- 2: Confirmed status, Player 3 wins
+    ('Darts', 3, 4, '2024-11-26 16:00:00', 'confirmed', 3, 2, TRUE, TRUE, 3),
+-- 3: Completed status, Player 6 wins
+    ('TableTennis', 1, 2, '2024-11-27 18:00:00', 'completed', 21, 18, TRUE, TRUE, 2),
+-- 4: Pending status
+    ('Pools', 2, 1, '2024-11-28 20:00:00', 'pending', 0, 0, FALSE, FALSE, NULL),
+-- 5: Completed status, Player 9 wins
+    ('Darts', 1, 2, '2024-11-29 15:30:00', 'completed', 300, 280, TRUE, TRUE, 2),
+-- 6: Confirmed status, Player 1 wins
+    ('TableTennis', 1, 2, '2024-11-30 17:00:00', 'confirmed', 3, 1, TRUE, TRUE, 1),
+    ('TableTennis', null, null, '2024-12-20 17:00:00', 'pending', 0, 0, false, false, null),
+    ('TableTennis', null, null, '2024-12-21 17:00:00', 'pending', 0, 0, false, false, null),
+    ('Pools', null, null, '2024-12-22 17:00:00', 'pending', 0, 0, false, false, null),
+    ('Pools', null, null, '2024-12-23 17:00:00', 'pending', 0, 0, false, false, null),
+    ('Pools', null, null, '2024-12-24 17:00:00', 'pending', 0, 0, false, false, null),
+    ('Darts', null, null, '2024-12-25 17:00:00', 'pending', 0, 0, false, false, null),
+    ('Darts', null, null, '2024-12-26 17:00:00', 'pending', 0, 0, false, false, null),
+    ('Darts', null, null, '2024-12-27 17:00:00', 'pending', 0, 0, false, false, null);
+INSERT INTO ranking (user_id,username,sport, wins) VALUES
+                                                (1, 'shy','Pools', 0),
+                                                (1, 'shy','Darts', 0),
+                                                (1, 'shy','TableTennis', 0),
+                                                (2, 'zyb','Pools', 0),
+                                                (2, 'zyb','Darts', 0),
+                                                (2, 'zyb','TableTennis', 0),
+                                                (3, 'xzc','Pools', 0),
+                                                (3, 'xzc','Darts', 0),
+                                                (3, 'xzc','TableTennis', 0),
+                                                (4, 'xx','Pools', 0),
+                                                (4, 'xx','Darts', 0),
+                                                (4, 'xx','TableTennis', 0);
+
diff --git a/src/main/resources/static/js/navBar.js b/src/main/resources/static/js/navBar.js
index 9976453a376e89ad01838efb3626197bd0629f6f..6dbb34ead8a21c7a9ff91fd68977fd3c5f11e65c 100644
--- a/src/main/resources/static/js/navBar.js
+++ b/src/main/resources/static/js/navBar.js
@@ -19,7 +19,7 @@ function loadNavbar(targetElementId) {
             document.getElementById(targetElementId).innerHTML = data;
 
             // After the navbar is loaded, check and update user authentication state
-            updateAuthButtons();
+            // updateAuthButtons();
         })
         .catch(error => {
             console.error('loadNav errors:', error);
@@ -27,34 +27,34 @@ function loadNavbar(targetElementId) {
 }
 
 // Update authentication buttons based on user login state
-function updateAuthButtons() {
-    fetch('/users/current', { method: 'GET' })
-        .then(response => {
-            if (!response.ok) {
-                throw new Error('User not logged in');
-            }
-            return response.json();
-        })
-        .then(data => {
-            // If logged in, update Login button to display username
-            const authBtn = document.getElementById('auth-btn');
-            authBtn.textContent = data.username;
-            authBtn.href = '#'; // Disable navigation for username display
-            authBtn.classList.remove('login-btn');
-            authBtn.classList.add('username-btn');
-
-            // Show the Logout button
-            document.getElementById('logout-btn').style.display = 'block';
-        })
-        .catch(() => {
-            // If not logged in, reset the buttons to default state
-            const authBtn = document.getElementById('auth-btn');
-            authBtn.textContent = 'Login';
-            authBtn.href = '/html/login.html'; // Link back to the login page
-            authBtn.classList.add('login-btn');
-            authBtn.classList.remove('username-btn');
-
-            // Hide the Logout button
-            document.getElementById('logout-btn').style.display = 'none';
-        });
-}
+// function updateAuthButtons() {
+//     fetch('/users/current', { method: 'GET' })
+//         .then(response => {
+//             if (!response.ok) {
+//                 throw new Error('User not logged in');
+//             }
+//             return response.json();
+//         })
+//         .then(data => {
+//             // If logged in, update Login button to display username
+//             const authBtn = document.getElementById('auth-btn');
+//             authBtn.textContent = data.username;
+//             authBtn.href = '#'; // Disable navigation for username display
+//             authBtn.classList.remove('login-btn');
+//             authBtn.classList.add('username-btn');
+//
+//             // Show the Logout button
+//             document.getElementById('logout-btn').style.display = 'block';
+//         })
+//         .catch(() => {
+//             // If not logged in, reset the buttons to default state
+//             const authBtn = document.getElementById('auth-btn');
+//             authBtn.textContent = 'Login';
+//             authBtn.href = '/html/login.html'; // Link back to the login page
+//             authBtn.classList.add('login-btn');
+//             authBtn.classList.remove('username-btn');
+//
+//             // Hide the Logout button
+//             document.getElementById('logout-btn').style.display = 'none';
+//         });
+// }
diff --git a/src/main/resources/templates/page/index.html b/src/main/resources/templates/page/index.html
index 2df5667ab42468fb9c2b1df0694dbad0445cb868..641d21dd6e5e3410e3c6c4a9f3950744ca56a352 100644
--- a/src/main/resources/templates/page/index.html
+++ b/src/main/resources/templates/page/index.html
@@ -6,6 +6,6 @@
     <link rel="stylesheet" href="../../css/index.css">
 </head>
 <body>
-        <p>index.html</p>
+        <p>index.html1</p>
 </body>
 </html>
\ No newline at end of file
diff --git a/src/test/java/sportsLeagueTest.side b/src/test/java/sportsLeagueTest.side
new file mode 100644
index 0000000000000000000000000000000000000000..7e7680563751bd081d7b98650e5f1db1d50d7955
--- /dev/null
+++ b/src/test/java/sportsLeagueTest.side
@@ -0,0 +1,346 @@
+{
+  "id": "16ffd25e-f706-4a5e-812c-fbb9b11e75cc",
+  "version": "2.0",
+  "name": "sportsLeagueTest",
+  "url": "http://localhost:8080",
+  "tests": [{
+    "id": "0664bd87-4218-41d5-833e-1d7ce52c2608",
+    "name": "testLoginandMatchTest",
+    "commands": [{
+      "id": "209c5b55-a832-4a2b-9372-e728e2c7aca6",
+      "comment": "",
+      "command": "open",
+      "target": "/html/login.html",
+      "targets": [],
+      "value": ""
+    }, {
+      "id": "5c887cdc-2af2-4872-b805-045796a73e08",
+      "comment": "",
+      "command": "setWindowSize",
+      "target": "652x672",
+      "targets": [],
+      "value": ""
+    }, {
+      "id": "6159b882-bf7e-4a0c-a26a-699e36d738fd",
+      "comment": "",
+      "command": "click",
+      "target": "id=email",
+      "targets": [
+        ["id=email", "id"],
+        ["name=email", "name"],
+        ["css=#email", "css:finder"],
+        ["xpath=//input[@id='email']", "xpath:attributes"],
+        ["xpath=//form[@id='loginForm']/input", "xpath:idRelative"],
+        ["xpath=//input", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "b4176d53-16e3-4162-8c95-d4dbf0a4ba3c",
+      "comment": "",
+      "command": "type",
+      "target": "id=email",
+      "targets": [
+        ["id=email", "id"],
+        ["name=email", "name"],
+        ["css=#email", "css:finder"],
+        ["xpath=//input[@id='email']", "xpath:attributes"],
+        ["xpath=//form[@id='loginForm']/input", "xpath:idRelative"],
+        ["xpath=//input", "xpath:position"]
+      ],
+      "value": "shy@creditsafe.com"
+    }, {
+      "id": "8248a227-e5d9-43e7-859a-aff3c895e870",
+      "comment": "",
+      "command": "click",
+      "target": "id=password",
+      "targets": [
+        ["id=password", "id"],
+        ["name=password", "name"],
+        ["css=#password", "css:finder"],
+        ["xpath=//input[@id='password']", "xpath:attributes"],
+        ["xpath=//form[@id='loginForm']/input[2]", "xpath:idRelative"],
+        ["xpath=//input[2]", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "73cfb95a-0326-48e4-b97a-06369d4eb339",
+      "comment": "",
+      "command": "type",
+      "target": "id=password",
+      "targets": [
+        ["id=password", "id"],
+        ["name=password", "name"],
+        ["css=#password", "css:finder"],
+        ["xpath=//input[@id='password']", "xpath:attributes"],
+        ["xpath=//form[@id='loginForm']/input[2]", "xpath:idRelative"],
+        ["xpath=//input[2]", "xpath:position"]
+      ],
+      "value": "shy"
+    }, {
+      "id": "44b3048d-06b1-4a31-be86-28dff3f52d98",
+      "comment": "",
+      "command": "click",
+      "target": "css=button",
+      "targets": [
+        ["css=button", "css:finder"],
+        ["xpath=//button[@type='submit']", "xpath:attributes"],
+        ["xpath=//form[@id='loginForm']/button", "xpath:idRelative"],
+        ["xpath=//button", "xpath:position"],
+        ["xpath=//button[contains(.,'Login')]", "xpath:innerText"]
+      ],
+      "value": ""
+    }, {
+      "id": "5f6090f4-5d83-417c-8a49-7b85e60a580b",
+      "comment": "",
+      "command": "click",
+      "target": "id=sportSelect",
+      "targets": [
+        ["id=sportSelect", "id"],
+        ["css=#sportSelect", "css:finder"],
+        ["xpath=//select[@id='sportSelect']", "xpath:attributes"],
+        ["xpath=//select", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "983cf95d-9003-4cda-9e7f-b6ad1b8873a8",
+      "comment": "",
+      "command": "select",
+      "target": "id=sportSelect",
+      "targets": [],
+      "value": "label=Pools"
+    }, {
+      "id": "b286ccc1-6cad-4701-b408-50086c7184a1",
+      "comment": "",
+      "command": "click",
+      "target": "id=sportSelect",
+      "targets": [
+        ["id=sportSelect", "id"],
+        ["css=#sportSelect", "css:finder"],
+        ["xpath=//select[@id='sportSelect']", "xpath:attributes"],
+        ["xpath=//select", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "1441088d-98fa-436d-b89c-923b26c5c272",
+      "comment": "",
+      "command": "select",
+      "target": "id=sportSelect",
+      "targets": [],
+      "value": "label=Darts"
+    }, {
+      "id": "565747ec-9749-4145-9313-0a5ae850264c",
+      "comment": "",
+      "command": "click",
+      "target": "id=sportSelect",
+      "targets": [
+        ["id=sportSelect", "id"],
+        ["css=#sportSelect", "css:finder"],
+        ["xpath=//select[@id='sportSelect']", "xpath:attributes"],
+        ["xpath=//select", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "7a51ec33-f510-4479-8aea-4a1f3522174a",
+      "comment": "",
+      "command": "select",
+      "target": "id=sportSelect",
+      "targets": [],
+      "value": "label=Table Tennis"
+    }, {
+      "id": "950a1838-f63d-4d69-a83f-01926ff620c2",
+      "comment": "",
+      "command": "click",
+      "target": "id=sportSelect",
+      "targets": [
+        ["id=sportSelect", "id"],
+        ["css=#sportSelect", "css:finder"],
+        ["xpath=//select[@id='sportSelect']", "xpath:attributes"],
+        ["xpath=//select", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "d5e44974-5e91-4255-8715-1794a9f6cbfe",
+      "comment": "",
+      "command": "select",
+      "target": "id=sportSelect",
+      "targets": [],
+      "value": "label=All Sports"
+    }, {
+      "id": "3d13848c-e203-471a-bc44-e0b8d60a8fd9",
+      "comment": "",
+      "command": "click",
+      "target": "id=statusSelect",
+      "targets": [
+        ["id=statusSelect", "id"],
+        ["css=#statusSelect", "css:finder"],
+        ["xpath=//select[@id='statusSelect']", "xpath:attributes"],
+        ["xpath=//select[2]", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "8e084da0-e59b-4ac9-b50e-dc609086e34e",
+      "comment": "",
+      "command": "select",
+      "target": "id=statusSelect",
+      "targets": [],
+      "value": "label=Pending"
+    }, {
+      "id": "e023b21b-ed4a-4454-9aab-a93a741636f8",
+      "comment": "",
+      "command": "click",
+      "target": "id=statusSelect",
+      "targets": [
+        ["id=statusSelect", "id"],
+        ["css=#statusSelect", "css:finder"],
+        ["xpath=//select[@id='statusSelect']", "xpath:attributes"],
+        ["xpath=//select[2]", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "cabca20b-76d7-493d-bee1-bf3dbaa34760",
+      "comment": "",
+      "command": "select",
+      "target": "id=statusSelect",
+      "targets": [],
+      "value": "label=Confirmed"
+    }, {
+      "id": "fb3a64d1-e62c-41db-acde-91dca7b84a39",
+      "comment": "",
+      "command": "click",
+      "target": "id=statusSelect",
+      "targets": [
+        ["id=statusSelect", "id"],
+        ["css=#statusSelect", "css:finder"],
+        ["xpath=//select[@id='statusSelect']", "xpath:attributes"],
+        ["xpath=//select[2]", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "b372f9e9-eb79-47c2-b82a-11cbdf162f93",
+      "comment": "",
+      "command": "select",
+      "target": "id=statusSelect",
+      "targets": [],
+      "value": "label=Completed"
+    }, {
+      "id": "d6905fbf-9814-49c2-bff6-b7cd4ffd0186",
+      "comment": "",
+      "command": "click",
+      "target": "id=statusSelect",
+      "targets": [
+        ["id=statusSelect", "id"],
+        ["css=#statusSelect", "css:finder"],
+        ["xpath=//select[@id='statusSelect']", "xpath:attributes"],
+        ["xpath=//select[2]", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "6eed2c7c-107f-48e9-a0dd-06ad5555caea",
+      "comment": "",
+      "command": "select",
+      "target": "id=statusSelect",
+      "targets": [],
+      "value": "label=All Sports"
+    }, {
+      "id": "0f56e0a9-75b0-40c7-a7c6-5ad2e4a07fd5",
+      "comment": "",
+      "command": "click",
+      "target": "id=sortTimeBtn",
+      "targets": [
+        ["id=sortTimeBtn", "id"],
+        ["css=#sortTimeBtn", "css:finder"],
+        ["xpath=//button[@id='sortTimeBtn']", "xpath:attributes"],
+        ["xpath=//div[2]/button", "xpath:position"],
+        ["xpath=//button[contains(.,'Sort by Time')]", "xpath:innerText"]
+      ],
+      "value": ""
+    }, {
+      "id": "0a21d7ae-0c6f-4136-94a6-8d544f42633c",
+      "comment": "",
+      "command": "click",
+      "target": "id=sortTimeBtn",
+      "targets": [
+        ["id=sortTimeBtn", "id"],
+        ["css=#sortTimeBtn", "css:finder"],
+        ["xpath=//button[@id='sortTimeBtn']", "xpath:attributes"],
+        ["xpath=//div[2]/button", "xpath:position"],
+        ["xpath=//button[contains(.,'Sort by Time')]", "xpath:innerText"]
+      ],
+      "value": ""
+    }, {
+      "id": "b985fc2e-1068-455b-b212-23c940790db5",
+      "comment": "",
+      "command": "click",
+      "target": "css=.match-item:nth-child(1)",
+      "targets": [
+        ["css=.match-item:nth-child(1)", "css:finder"],
+        ["xpath=//div[@id='matchesList']/div", "xpath:idRelative"],
+        ["xpath=//div[3]/div[3]/div", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "a02a6189-a333-4149-bd8c-696869367f06",
+      "comment": "",
+      "command": "click",
+      "target": "id=updateBtn",
+      "targets": [
+        ["id=updateBtn", "id"],
+        ["css=#updateBtn", "css:finder"],
+        ["xpath=//button[@id='updateBtn']", "xpath:attributes"],
+        ["xpath=//div[@id='matchDetails']/button", "xpath:idRelative"],
+        ["xpath=//button", "xpath:position"],
+        ["xpath=//button[contains(.,'Update Score')]", "xpath:innerText"]
+      ],
+      "value": ""
+    }, {
+      "id": "69365172-4d89-44d1-8959-1bf8d3f6db26",
+      "comment": "",
+      "command": "click",
+      "target": "id=matchDetails",
+      "targets": [
+        ["id=matchDetails", "id"],
+        ["css=#matchDetails", "css:finder"],
+        ["xpath=//div[@id='matchDetails']", "xpath:attributes"],
+        ["xpath=//div[3]/div", "xpath:position"]
+      ],
+      "value": ""
+    }, {
+      "id": "d3db7118-ff77-4602-8e55-fcb0c7d1264f",
+      "comment": "",
+      "command": "type",
+      "target": "id=scoreA",
+      "targets": [
+        ["id=scoreA", "id"],
+        ["name=scoreA", "name"],
+        ["css=#scoreA", "css:finder"],
+        ["xpath=//input[@id='scoreA']", "xpath:attributes"],
+        ["xpath=//form[@id='updateMatchForm']/input", "xpath:idRelative"],
+        ["xpath=//input", "xpath:position"]
+      ],
+      "value": "1"
+    }, {
+      "id": "d78e77a7-1fd2-4acc-a5b1-0f785d6b6ccc",
+      "comment": "",
+      "command": "click",
+      "target": "css=button:nth-child(23)",
+      "targets": [
+        ["css=button:nth-child(23)", "css:finder"],
+        ["xpath=//button[@type='submit']", "xpath:attributes"],
+        ["xpath=//form[@id='updateMatchForm']/button", "xpath:idRelative"],
+        ["xpath=//button", "xpath:position"],
+        ["xpath=//button[contains(.,'Update Score')]", "xpath:innerText"]
+      ],
+      "value": ""
+    }]
+  }],
+  "suites": [{
+    "id": "c5b624ea-1ae7-4147-b21a-7ebf68e013d9",
+    "name": "Default Suite",
+    "persistSession": false,
+    "parallel": false,
+    "timeout": 300,
+    "tests": ["0664bd87-4218-41d5-833e-1d7ce52c2608"]
+  }],
+  "urls": ["http://localhost:8080/"],
+  "plugins": []
+}
\ No newline at end of file
diff --git a/src/test/java/uk/ac/cf/spring/demo/ChromeDriverTest.java b/src/test/java/uk/ac/cf/spring/demo/ChromeDriverTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..749fb606ac60d3d3c230eb204fbe74e31173a390
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/ChromeDriverTest.java
@@ -0,0 +1,69 @@
+//package uk.ac.cf.spring.demo;
+//import org.junit.jupiter.api.AfterEach;
+//import org.junit.jupiter.api.BeforeAll;
+//import org.junit.jupiter.api.BeforeEach;
+//import org.junit.jupiter.api.Test;
+//import org.junit.runner.RunWith;
+//import org.openqa.selenium.By;
+//import org.openqa.selenium.WebDriver;
+//import org.openqa.selenium.chrome.ChromeDriver;
+//import org.openqa.selenium.chrome.ChromeOptions;
+//import org.openqa.selenium.firefox.FirefoxDriver;
+//import org.openqa.selenium.firefox.FirefoxOptions;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.boot.test.context.SpringBootTest;
+//import org.springframework.test.context.junit4.SpringRunner;
+//
+//import io.github.bonigarcia.wdm.WebDriverManager;
+//
+//
+//import static org.junit.Assert.assertTrue;
+//@RunWith(SpringRunner.class)
+//@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+//public class ChromeDriverTest {
+//
+//        @Value("${local.server.port}")
+//        private int port;
+//         WebDriver webDriver;
+//        @BeforeEach
+//        void setupTest() {
+////            ChromeOptions options = new ChromeOptions();
+//            FirefoxOptions options = new FirefoxOptions();
+////            options.addArguments("--remote-debugging-port=42227");
+//            options.addArguments("--headless");
+////            webDriver = new ChromeDriver(options);
+//            webDriver = new FirefoxDriver(options);
+//        }
+//
+//        @AfterEach
+//        public void teardown() {
+//            // 关闭浏览器
+//            if (webDriver != null) {
+//                webDriver.quit();
+//            }
+//        }
+//
+//        @Test
+//        public void greetingShouldReturnDefaultMessage() throws Exception {
+//            webDriver.get("http://localhost:" + port + "/html/matchSchedule.html");
+//
+//            assertTrue(webDriver.getPageSource().contains("Sport"));
+//        }
+//        @Test
+//        public void loginAndSeeLoginMessage() throws Exception {
+//        webDriver.get("http://localhost:" + port + "/html/login.html");
+//
+//        webDriver.findElement(By.name("email")).sendKeys("shy@creditsafe.com");
+//        webDriver.findElement(By.name("password")).sendKeys("shy");
+//
+//        webDriver.findElement(By.xpath("//button[text()='Login']")).click();
+//
+//        Thread.sleep(2000);
+//
+//        assertTrue(webDriver.getPageSource().contains("Login successful"));
+//    }
+//    }
+//
+
+
diff --git a/src/test/java/uk/ac/cf/spring/demo/HTTPConnectionTest.java b/src/test/java/uk/ac/cf/spring/demo/HTTPConnectionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0029d1cb920e9313f871b7a28ec24a2b87b1b1a0
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/HTTPConnectionTest.java
@@ -0,0 +1,64 @@
+package uk.ac.cf.spring.demo;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.ResponseEntity;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class HTTPConnectionTest {
+
+    // 通过 Spring 注入的端口
+    @Value(value = "${local.server.port}")
+    private int port;
+
+    // RestTemplate 用于发送 HTTP 请求
+    @Autowired
+    private TestRestTemplate restTemplate;
+
+    // 提取常量,避免在每个测试方法中重复
+    private String baseUrl() {
+        return "http://localhost:" + port;
+    }
+
+    // 测试页面加载是否包含指定内容
+    @Test
+    public void greetingShouldReturnDefaultMessage() throws Exception {
+        String url = baseUrl() + "/html/matchSchedule.html";
+
+        // 使用 RestTemplate 发送 GET 请求并检查返回内容
+        String response = this.restTemplate.getForObject(url, String.class);
+
+        // 检查响应中是否包含 "Sport" 字样
+        assertThat(response).contains("Sport");
+    }
+
+    // 测试数据库接口(模拟用户登录并检查后续请求)
+    @Test
+    public void dbMatchTest() throws Exception {
+        String loginUrl = baseUrl() + "/api/users/login";
+        String matchUrl = baseUrl() + "/match";
+
+        // 准备登录数据
+        Map<String, String> loginData = new HashMap<>();
+        loginData.put("email", "shy@creditsafe.com");
+        loginData.put("password", "shy");
+
+        // 发送 POST 请求模拟登录
+        ResponseEntity<String> loginResponse = this.restTemplate.postForEntity(loginUrl, loginData, String.class);
+
+        // 验证登录成功消息
+        assertThat(loginResponse.getBody()).contains("Login successful");
+
+        // 检查 match 接口返回的内容是否包含 "Pools" 字段
+        String matchResponse = this.restTemplate.getForObject(matchUrl, String.class);
+        assertThat(matchResponse).contains("Pools");
+    }
+}
diff --git a/src/test/java/uk/ac/cf/spring/demo/LightweightMockMVCTests.java b/src/test/java/uk/ac/cf/spring/demo/LightweightMockMVCTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f5823289384e01a17b77426ced21b7d78b72162
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/LightweightMockMVCTests.java
@@ -0,0 +1,109 @@
+package uk.ac.cf.spring.demo;
+
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import uk.ac.cf.spring.demo.sports.match.MatchController;
+import uk.ac.cf.spring.demo.sports.match.MatchItem;
+import uk.ac.cf.spring.demo.sports.match.MatchService;
+import uk.ac.cf.spring.demo.sports.security.SecurityConfig;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.Mockito.when;
+//Lightweight Mock MVC
+// Unit or Integration
+
+@RunWith(SpringRunner.class)
+@WebMvcTest(MatchController.class)
+@Import(SecurityConfig.class)
+public class LightweightMockMVCTests {
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @MockBean
+    private MatchService matchService;
+    @WithMockUser(username = "shy", password = "shy")
+    @Test
+    public void testGetAllMatchItems() throws Exception {
+        // Prepare match items with specific data
+        MatchItem matchItem1 = new MatchItem(1L, "Pools", 1L, 2L, LocalDateTime.now(), "pending", 0L, 0L, false, false, 0L);
+        MatchItem matchItem2 = new MatchItem(2L, "Darts", 3L, 4L, LocalDateTime.now(), "pending", 0L, 0L, false, false, 0L);
+
+        List<MatchItem> matchItems = Arrays.asList(matchItem1, matchItem2);
+
+        // Mocking the service method
+        when(matchService.getMatchItems()).thenReturn(matchItems);
+
+        // Perform the GET request and verify the result
+        mockMvc.perform(MockMvcRequestBuilders.get("/match"))
+                .andExpect(MockMvcResultMatchers.status().isOk())
+                .andExpect(MockMvcResultMatchers.jsonPath("$[0].id").value(1))
+                .andExpect(MockMvcResultMatchers.jsonPath("$[0].sport").value("Pools"))
+                .andExpect(MockMvcResultMatchers.jsonPath("$[1].id").value(2))
+                .andExpect(MockMvcResultMatchers.jsonPath("$[1].sport").value("Darts"));
+    }
+
+    @WithMockUser(username = "shy", password = "shy")
+    @Test
+    public void testGetMatchItemById() throws Exception {
+        // Prepare a specific match item
+        MatchItem matchItem = new MatchItem(1L, "Pools", 1L, 2L, LocalDateTime.now(), "pending", 0L, 0L, false, false, 0L);
+
+        // Mocking the service method
+        when(matchService.getMatchItemById(1L)).thenReturn(matchItem);
+
+        // Perform the GET request and verify the result
+        mockMvc.perform(MockMvcRequestBuilders.get("/match/1"))
+                .andExpect(MockMvcResultMatchers.status().isOk())
+                .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(1))
+                .andExpect(MockMvcResultMatchers.jsonPath("$.sport").value("Pools"));
+    }
+    @WithMockUser(username = "shy", password = "shy")
+    @Test
+    public void testAddMatchItem() throws Exception {
+        // Prepare the match item to be added
+        MatchItem matchItem = new MatchItem(null, "Tennis", 1L, 2L, LocalDateTime.now(), "pending", 0L, 0L, false, false, 0L);
+
+        // Perform the POST request
+        mockMvc.perform(MockMvcRequestBuilders.post("/match")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content("{\"sport\":\"Tennis\",\"team1\":1,\"team2\":2,\"date\":\"" + LocalDateTime.now() + "\",\"status\":\"pending\",\"score1\":0,\"score2\":0,\"isLive\":false,\"isFinished\":false,\"duration\":0}"))
+                .andExpect(MockMvcResultMatchers.status().isOk());
+    }
+    @WithMockUser(username = "shy", password = "shy")
+    @Test
+    public void testUpdateMatchItem() throws Exception {
+        // Prepare the match item to be updated
+        MatchItem matchItem = new MatchItem(1L, "Pools", 1L, 2L, LocalDateTime.now(), "pending", 0L, 0L, false, false, 0L);
+
+        // Mocking the service method
+        when(matchService.getMatchItemById(1L)).thenReturn(matchItem);
+
+        // Perform the PUT request
+        mockMvc.perform(MockMvcRequestBuilders.put("/match/1")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content("{\"id\":1,\"sport\":\"Pools\",\"team1\":1,\"team2\":2,\"date\":\"" + LocalDateTime.now() + "\",\"status\":\"pending\",\"score1\":0,\"score2\":0,\"isLive\":false,\"isFinished\":false,\"duration\":0}"))
+                .andExpect(MockMvcResultMatchers.status().isOk());
+    }
+    @WithMockUser(username = "shy", password = "shy")
+    @Test
+    public void testDeleteMatchItem() throws Exception {
+        // Perform the DELETE request
+        mockMvc.perform(MockMvcRequestBuilders.delete("/match/1"))
+                .andExpect(MockMvcResultMatchers.status().isOk());
+    }
+}
+
diff --git a/src/test/java/uk/ac/cf/spring/demo/MatchControllerIngrationTest.java b/src/test/java/uk/ac/cf/spring/demo/MatchControllerIngrationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f651d128885d906fc9798a1981fc6d6e9346fd8e
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/MatchControllerIngrationTest.java
@@ -0,0 +1,62 @@
+package uk.ac.cf.spring.demo;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import uk.ac.cf.spring.demo.sports.match.MatchController;
+import uk.ac.cf.spring.demo.sports.match.MatchItem;
+import uk.ac.cf.spring.demo.sports.match.MatchService;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+//Full container Mock MVC Integration
+@SpringBootTest
+@AutoConfigureMockMvc
+public class MatchControllerIngrationTest {
+
+    @MockBean
+    private MatchService matchService;
+
+//    @InjectMocks
+//    private MatchController matchController;
+
+    @Autowired
+    private MockMvc mockMvc;
+    // Integration Test with Mock MVC
+    @Test
+    void testGetAllMatches() throws Exception {
+        MatchItem match1 = new MatchItem(1L, "Soccer", 1L, 2L, null, "completed", 2L, 3L, true, true, 2L);
+        List<MatchItem> matches = Arrays.asList(match1);
+
+        when(matchService.getMatchItems()).thenReturn(matches);
+
+        mockMvc.perform(MockMvcRequestBuilders.get("/match"))
+                .andExpect(status().isOk());
+
+        verify(matchService, times(1)).getMatchItems();
+    }
+
+    //    Integration Test with Mock MVC
+    @Test
+    void testGetMatchById() throws Exception {
+        MatchItem match1 = new MatchItem(1L, "Soccer", 1L, 2L, null, "completed", 2L, 3L, true, true, 2L);
+
+        when(matchService.getMatchItemById(1L)).thenReturn(match1);
+
+        mockMvc.perform(MockMvcRequestBuilders.get("/match/1"))
+                .andExpect(status().isOk());
+
+        verify(matchService, times(1)).getMatchItemById(1L);
+    }
+
+    //
+}
diff --git a/src/test/java/uk/ac/cf/spring/demo/TrueUnitTests.java b/src/test/java/uk/ac/cf/spring/demo/TrueUnitTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..5671d6f0be266bd6fdfe4c6d52401efe7f7a0500
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/TrueUnitTests.java
@@ -0,0 +1,61 @@
+package uk.ac.cf.spring.demo;
+
+import org.junit.jupiter.api.Test;
+import uk.ac.cf.spring.demo.sports.match.MatchItem;
+
+import java.time.LocalDateTime;
+public class TrueUnitTests {
+    //    测试MatchItem的getter和setter方法
+    @Test
+    public void testGettersAndSetters() {
+        // 创建一个 MatchItem 对象
+        MatchItem matchItem = new MatchItem();
+
+        // 测试 id 的 getter 和 setter
+        matchItem.setId(1L);
+        assert matchItem.getId() == 1L : "Test failed for id";
+
+        // 测试 sport 的 getter 和 setter
+        matchItem.setSport("football");
+        assert "football".equals(matchItem.getSport()) : "Test failed for sport";
+
+        // 测试 playerAId 的 getter 和 setter
+        matchItem.setPlayerAId(10L);
+        assert matchItem.getPlayerAId() == 10L : "Test failed for playerAId";
+
+        // 测试 playerBId 的 getter 和 setter
+        matchItem.setPlayerBId(20L);
+        assert matchItem.getPlayerBId() == 20L : "Test failed for playerBId";
+
+        // 测试 PlanTime 的 getter 和 setter
+        LocalDateTime now = LocalDateTime.now();
+        matchItem.setPlanTime(now);
+        assert now.equals(matchItem.getPlanTime()) : "Test failed for PlanTime";
+
+        // 测试 status 的 getter 和 setter
+        matchItem.setStatus("confirmed");
+        assert "confirmed".equals(matchItem.getStatus()) : "Test failed for status";
+
+        // 测试 scoreA 的 getter 和 setter
+        matchItem.setScoreA(5L);
+        assert matchItem.getScoreA() == 5L : "Test failed for scoreA";
+
+        // 测试 scoreB 的 getter 和 setter
+        matchItem.setScoreB(3L);
+        assert matchItem.getScoreB() == 3L : "Test failed for scoreB";
+
+        // 测试 confirmByA 的 getter 和 setter
+        matchItem.setConfirmByA(true);
+        assert matchItem.getConfirmByA() : "Test failed for confirmByA";
+
+        // 测试 confirmByB 的 getter 和 setter
+        matchItem.setConfirmByB(false);
+        assert !matchItem.getConfirmByB() : "Test failed for confirmByB";
+
+        // 测试 winnerId 的 getter 和 setter
+        matchItem.setWinnerId(10L);
+        assert matchItem.getWinnerId() == 10L : "Test failed for winnerId";
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/uk/ac/cf/spring/demo/user/controller/DriverPageTests.java b/src/test/java/uk/ac/cf/spring/demo/user/controller/DriverPageTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..b57ee37280f1d4f8306485bf465a2c34f391288e
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/user/controller/DriverPageTests.java
@@ -0,0 +1,94 @@
+package uk.ac.cf.spring.demo.user.controller;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.runner.RunWith;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.firefox.FirefoxOptions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.context.junit4.SpringRunner;
+import io.github.bonigarcia.wdm.WebDriverManager;
+
+import java.io.File;
+import java.time.Duration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertTrue;
+
+//@RunWith(SpringRunner.class)
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class DriverPageTests {
+
+    @Value("${local.server.port}")
+    private int port;
+
+    WebDriver webDriver;
+//    private WebDriverWait wait;
+
+    @BeforeAll
+    public static void setupClass() {
+        WebDriverManager.firefoxdriver().setup();
+    }
+
+    @BeforeEach
+    public void setupTest() {
+        // Get the current working directory
+        String currentDir = System.getProperty("user.dir");
+
+        // Set the path to the local geckodriver executable
+        System.setProperty("webdriver.gecko.driver", currentDir + File.separator + "geckodriver.exe");
+        FirefoxOptions options = new FirefoxOptions();
+        options.addArguments("--headless");
+        webDriver = new FirefoxDriver(options);
+//        wait = new WebDriverWait(webDriver, Duration.ofSeconds(10));
+    }
+
+    @AfterEach
+    public void teardown() {
+        if (webDriver != null) {
+            webDriver.quit();
+        }
+    }
+
+    @Test
+    public void testUserManagementPage() {
+        // Test fetching and displaying users
+        webDriver.get("http://localhost:" + port + "/html/backGroundUser.html");
+//        assertTrue(webDriver.findElement(By.cssSelector("#userTable")).isDisplayed());
+
+        // Test adding a new user
+        webDriver.findElement(By.id("username")).sendKeys("shy");
+        webDriver.findElement(By.id("email")).sendKeys("shy@creditsafe.com");
+        webDriver.findElement(By.id("password")).sendKeys("$2a$16$R0aSGzbklUhpRfIMhocewephgUDMFOffLb7faSwwHusqHh81G026i");
+        webDriver.findElement(By.id("role")).sendKeys("USER");
+        webDriver.findElement(By.xpath("//button[text()='Add User']")).click();
+        assertTrue(webDriver.findElement(By.cssSelector("#userTable")).getText().contains("shy"));
+
+        // Test editing a user
+        webDriver.findElement(By.xpath("//tr[td[text()='shy']]//button[text()='Edit']")).click();
+        webDriver.findElement(By.id("email")).clear();
+        webDriver.findElement(By.id("email")).sendKeys("upu@creditsafe.com");
+        webDriver.findElement(By.xpath("//button[text()='Update User']")).click();
+        assertTrue(webDriver.findElement(By.cssSelector("#userTable")).getText().contains("upu@creditsafe.com"));
+
+        // Test deleting a user
+        webDriver.findElement(By.xpath("//tr[td[text()='shy']]//button[text()='Delete']")).click();
+        assertThat(webDriver.findElement(By.cssSelector("#userTable")).getText()).doesNotContain("shy");
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/uk/ac/cf/spring/demo/user/controller/HTTPConnectionTest.java b/src/test/java/uk/ac/cf/spring/demo/user/controller/HTTPConnectionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3027ab489ced2c79e81738893b5d217252815da0
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/user/controller/HTTPConnectionTest.java
@@ -0,0 +1,29 @@
+package uk.ac.cf.spring.demo.user.controller;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.http.ResponseEntity;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class HTTPConnectionTest {
+
+    @LocalServerPort
+    private int port;
+
+    @Autowired
+    private TestRestTemplate restTemplate;
+
+    @Test
+    public void testBackGroundUserPage() {
+        String url = "http://localhost:" + port + "/html/backGroundUser.html";
+        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
+
+        assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
+        assertThat(response.getBody()).contains("<title>User Management</title>");
+    }
+}
diff --git a/src/test/java/uk/ac/cf/spring/demo/user/controller/RegistrationAndLoginTest.java b/src/test/java/uk/ac/cf/spring/demo/user/controller/RegistrationAndLoginTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..72952ef63457ff7da70323557a93d073b134debd
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/user/controller/RegistrationAndLoginTest.java
@@ -0,0 +1,115 @@
+package uk.ac.cf.spring.demo.user.controller;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+//import org.junit.runner.RunWith;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+//import org.openqa.selenium.chrome.ChromeDriver;
+//import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.firefox.FirefoxOptions;
+import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+//import io.github.bonigarcia.wdm.WebDriverManager;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@ExtendWith(SpringExtension.class)
+@AutoConfigureMockMvc
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class RegistrationAndLoginTest {
+
+//    @Value("${local.server.port}")
+//    private int port;
+
+    //    an alternative really good resource:   https://github.com/bonigarcia/webdrivermanager
+
+    @Autowired
+    private MockMvc mockMvc;
+    WebDriver webDriver;
+
+    @BeforeAll
+    static void setupClass() {
+        // set the path of geckodriver
+        System.setProperty("webdriver.gecko.driver", "C:\\UniProjects\\geckodriver-v0.35.0-win64\\geckodriver.exe");
+    }
+
+//    @BeforeAll
+//    static void setupClass() {
+//        WebDriverManager.firefoxdriver().setup();
+//    }
+
+    @BeforeEach
+    void setupTest() {
+        FirefoxOptions options = new FirefoxOptions();
+//        options.addArguments("--remote-debugging-port=42227");
+        options.addArguments("--headless");
+        webDriver = new FirefoxDriver(options);
+    }
+
+    @AfterEach
+    void teardown() {
+        // Close the browser after each test
+        if (webDriver != null) {
+            webDriver.quit();
+        }
+    }
+
+    @Test
+    public void testUserRegistrationAndLogin() throws Exception {
+        // Step 1: Navigate to the registration page
+        webDriver.get("http://localhost:8080/html/register.html");
+
+        // Fill out the registration form
+        webDriver.findElement(By.id("username")).sendKeys("testuser");
+        webDriver.findElement(By.id("email")).sendKeys("testuser@creditsafe.com");
+        webDriver.findElement(By.id("password")).sendKeys("password123");
+        webDriver.findElement(By.cssSelector("button[type='submit']")).click();
+
+        // Wait for the registration to complete and redirect to the login page
+        Thread.sleep(2000); // Simple wait for demonstration purposes
+
+        // Step 2: Navigate to the login page
+        webDriver.get("http://localhost:8080/html/login.html");
+
+        // Fill out the login form
+        webDriver.findElement(By.id("email")).sendKeys("testuser@creditsafe.com");
+        webDriver.findElement(By.id("password")).sendKeys("password123");
+        webDriver.findElement(By.cssSelector("button[type='submit']")).click();
+
+        // Wait for the login to complete and redirect to the match schedule page
+        Thread.sleep(2000); // Simple wait for demonstration purposes
+
+        // Verify that the user is redirected to the match schedule page
+        String currentUrl = webDriver.getCurrentUrl();
+        assert currentUrl.endsWith("/html/matchSchedule.html");
+    }
+
+    @Test
+    public void testLoginWithInvalidCredentials() throws Exception {
+        // Step 1: Navigate to the login page
+        webDriver.get("http://localhost:8080/html/login.html");
+
+        // Fill out the login form with invalid credentials
+        webDriver.findElement(By.id("email")).sendKeys("invalid@example.com");
+        webDriver.findElement(By.id("password")).sendKeys("wrongpassword");
+        webDriver.findElement(By.cssSelector("button[type='submit']")).click();
+
+        // Wait for the error message to appear
+        Thread.sleep(2000); // Simple wait for demonstration purposes
+
+        // Verify that the error message is displayed
+        String errorMessage = webDriver.findElement(By.id("message")).getText();
+        assert errorMessage.contains("Invalid email or password");
+    }
+
+}
diff --git a/src/test/java/uk/ac/cf/spring/demo/user/controller/RegistrationControllerMockMVCTest.java b/src/test/java/uk/ac/cf/spring/demo/user/controller/RegistrationControllerMockMVCTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c703773fd84a2120867b11f5a9386afb13dcb0dc
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/user/controller/RegistrationControllerMockMVCTest.java
@@ -0,0 +1,82 @@
+package uk.ac.cf.spring.demo.user.controller;
+
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import org.springframework.boot.test.mock.mockito.MockBean;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import uk.ac.cf.spring.demo.user.*;
+
+@SpringBootTest
+@AutoConfigureMockMvc
+//@WebMvcTest(RegistrationController.class)
+public class RegistrationControllerMockMVCTest {
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @MockBean
+    private UserRepository userRepository;
+
+    private ObjectMapper objectMapper;
+
+    @BeforeEach
+    public void setUp() {
+        MockitoAnnotations.openMocks(this);
+        objectMapper = new ObjectMapper();  // Initialize ObjectMapper
+    }
+
+    @Test
+    public void testShowRegistrationForm() throws Exception {
+        mockMvc.perform(get("/register"))
+                .andDo(print())
+                .andExpect(status().isOk())
+                .andExpect(view().name("html/register.html"));
+    }
+
+    @Test
+    public void testRegisterUser_Success() throws Exception {
+        User user = new User(null, "dummy", "dummy@creditsafe.com", "password", "USER");
+
+        when(userRepository.existsByUsername(user.getUsername())).thenReturn(false);
+        when(userRepository.existsByEmail(user.getEmail())).thenReturn(false);
+
+        mockMvc.perform(post("/api/users/register")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(objectMapper.writeValueAsString(user)))
+                .andExpect(status().isOk())
+                .andExpect(content().json("{\"message\": \"Registration successful\"}"));
+
+        verify(userRepository, times(1)).save(user); // Verifying the save method is called once with the user object.
+    }
+
+    @Test
+    public void testRegisterUser_UsernameExists() throws Exception {
+        User user = new User(null, "dummy", "dummy@creditsafe.com", "password", "USER");
+
+        when(userRepository.existsByUsername(user.getUsername())).thenReturn(true);
+
+        mockMvc.perform(post("/api/users/register")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(objectMapper.writeValueAsString(user)))
+                .andExpect(status().isBadRequest())
+                .andExpect(content().json("{\"message\": \"Username already exists\"}"));
+
+        verify(userRepository, never()).save(user); // Verify that save is not called when username exists.
+    }
+
+}
+
diff --git a/src/test/java/uk/ac/cf/spring/demo/user/controller/RegistrationControllerTest.java b/src/test/java/uk/ac/cf/spring/demo/user/controller/RegistrationControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..de7c2ea04ea9b391e35d454c128f72f566c114f5
--- /dev/null
+++ b/src/test/java/uk/ac/cf/spring/demo/user/controller/RegistrationControllerTest.java
@@ -0,0 +1,26 @@
+package uk.ac.cf.spring.demo.user.controller;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class RegistrationControllerTest {
+
+    @BeforeEach
+    void setUp() {
+    }
+
+    @AfterEach
+    void tearDown() {
+    }
+
+    @Test
+    void showRegistrationForm() {
+    }
+
+    @Test
+    void registerUser() {
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/uk/ac/cf/spring/demo/user/controller/geckodriver.exe b/src/test/java/uk/ac/cf/spring/demo/user/controller/geckodriver.exe
new file mode 100644
index 0000000000000000000000000000000000000000..5ee87292587d85f50c28dca210111940f76c6511
Binary files /dev/null and b/src/test/java/uk/ac/cf/spring/demo/user/controller/geckodriver.exe differ