From b7aa025e0ee57c15d0d5893402d18e22ef1ba969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fern=C3=A1ndez=20Noriega?= Date: Fri, 26 Apr 2024 13:11:06 +0200 Subject: [PATCH 01/14] Added general description of the project in introduction --- docs/src/01_introduction_and_goals.adoc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/src/01_introduction_and_goals.adoc b/docs/src/01_introduction_and_goals.adoc index d1c8833..9824bde 100644 --- a/docs/src/01_introduction_and_goals.adoc +++ b/docs/src/01_introduction_and_goals.adoc @@ -15,7 +15,12 @@ These include * quality goals for the architecture and * relevant stakeholders and their expectations **** -In these points, the main goals and functional requirements will be explained. In order to give context on how the webapp will be developed. + +This project's aim, in summary, is developing a public web application which has a game consisting on answering multiple choice questions +with one correct answer generated using Wikidata. For reference of the general mechanic, see the spanish quiz program 'Saber y Ganar' and +its game mode 'Descartando' + +Next, important basic aspects of the project will be described in order to give context on how the webapp will be developed. === Requirements Overview @@ -142,10 +147,9 @@ Table with role names, person names, and their expectations with respect to the [options="header",cols="1,2,2"] |=== -|Role/Name|Contact|Expectations +|Role/Name|Members|Expectations | *Students* | Andrés Cadenas Blanco, Christian Fernandez Noriega , Adrián González Guadalupe and Luis Salvador Ferrero | Are the ones in charge of web development. They will work together to make the application. | *Teachers* | Pablo González | In charge of supervising the student's teamwork, ensuring the work accomplishes the goals in the best way possible and helping in the development and solving doubts. -| *Bussineses* | RTve has hired software development company HappySw | Emphasis the SOLID part of the web and have a high understanding of this area | *Users* | Anyone that wants to use the web | They should be able to understand how to use and move around the web with ease |=== From 904bc7f08c2810219a173f8559da1cecebab3359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fern=C3=A1ndez=20Noriega?= Date: Fri, 26 Apr 2024 13:41:56 +0200 Subject: [PATCH 02/14] Small adjustments to system scope and context --- docs/src/03_system_scope_and_context.adoc | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/src/03_system_scope_and_context.adoc b/docs/src/03_system_scope_and_context.adoc index c74e0fe..b071f32 100644 --- a/docs/src/03_system_scope_and_context.adoc +++ b/docs/src/03_system_scope_and_context.adoc @@ -60,11 +60,11 @@ actor Player [WIQ Game] <> [UsersAPI] <> -[questionsAPI] <> +[QuestionsAPI] <> Player ..> (WIQ Game) : register/login -[questionsAPI] ..> Wikidata +[QuestionsAPI] ..> Wikidata [WIQ Game] ..> UsersAPI -[WIQ Game] ..> questionsAPI +[WIQ Game] ..> QuestionsAPI ---- [cols="e,2e" options="header"] @@ -80,12 +80,9 @@ Player ..> (WIQ Game) : register/login |Wikidata |External data repository from which questions are generated -|MongoDB -|Database for storing players' info and scores - |Users Info API |Manages data of users, both registration/login data and their past scores -|questions API +|Questions API |Manages generation of questions from Wikidata |=== \ No newline at end of file From 3dabd4255dbfdb9ded53895c8802d66434e064a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fern=C3=A1ndez=20Noriega?= Date: Fri, 26 Apr 2024 13:57:34 +0200 Subject: [PATCH 03/14] Removed mongodb from Question API white box as it does not use it --- docs/src/05_building_block_view.adoc | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/src/05_building_block_view.adoc b/docs/src/05_building_block_view.adoc index 8c804f4..cdf5bc8 100644 --- a/docs/src/05_building_block_view.adoc +++ b/docs/src/05_building_block_view.adoc @@ -74,11 +74,11 @@ actor Player rectangle "WIQ Game (Level 1)"{ [WIQ Game GUI] [UsersAPI] -[questionsAPI] #BurlyWood +[QuestionsAPI] #BurlyWood Player ..> (WIQ Game GUI) -[questionsAPI] ..> Wikidata +[QuestionsAPI] ..> Wikidata [WIQ Game GUI] ..> UsersAPI -[WIQ Game GUI] ..> questionsAPI +[WIQ Game GUI] ..> QuestionsAPI } ---- @@ -153,7 +153,7 @@ Please prefer relevance over completeness. Specify important, surprising, risky, Leave out normal, simple, boring or standardized parts of your system **** -==== questions API (White Box) +==== Questions API (White Box) This is the Component that holds the functionallity for the main purpose of the webapp: Allowing players to see questions and answer them, getting a consequent score update. @@ -163,17 +163,15 @@ answer them, getting a consequent score update. ...describes the internal structure of _building block 1_. **** -[plantuml,"questions API (WhiteBox)",png] +[plantuml,"Questions API (WhiteBox)",png] ---- [Wikidata] [wikibase-sdk] <> [WIQ Game GUI] -database MongoDB rectangle "questionsAPI (Level 2)"{ [question-service] ..> [wikibase-sdk] [question-service] ..> [Wikidata] -[question-service] <--> MongoDB [WIQ Game GUI] ..> [question-service] : new question [WIQ Game GUI] ..> [question-service] : validate answer } @@ -195,7 +193,4 @@ Contained Black boxes:: |wikibase-sdk |External library that facilitates and simplifies the use of wikidata for the generation of questions. -|MongoDB -|Data about users and their scores is stored here - |=== \ No newline at end of file From 9d1ed48674682a9d24a1521c9069bca612a0c7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fern=C3=A1ndez=20Noriega?= Date: Fri, 26 Apr 2024 14:06:03 +0200 Subject: [PATCH 04/14] Corrected placement of DB in runtime view playing diagram --- docs/src/06_runtime_view.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/06_runtime_view.adoc b/docs/src/06_runtime_view.adoc index 44d58d4..3264c92 100644 --- a/docs/src/06_runtime_view.adoc +++ b/docs/src/06_runtime_view.adoc @@ -39,9 +39,9 @@ GW -> WEBC: Send category statistics actor User entity WEBC as "Web Client" entity GW as "Gateway" -database DB as "MongoDB" entity QU as "Questions API" entity USERS as "Users Service API" +database DB as "MongoDB" User -> WEBC: Select a category to play WEBC -> GW: Get a question From f85bfc67c4f0cb984a35476e879f1d206d3e3a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fern=C3=A1ndez=20Noriega?= Date: Fri, 26 Apr 2024 14:10:42 +0200 Subject: [PATCH 05/14] Removed mongodb relation from and to question-service in deployment view --- docs/src/07_deployment_view.adoc | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/src/07_deployment_view.adoc b/docs/src/07_deployment_view.adoc index 0a8008b..2951a29 100644 --- a/docs/src/07_deployment_view.adoc +++ b/docs/src/07_deployment_view.adoc @@ -121,9 +121,6 @@ MDB-[dashed]->AS US-[dashed]->MDB MDB-[dashed]->US - -QS-[dashed]->MDB -MDB-[dashed]->QS ---- The architecture of WIQ is based on microservices. Gateway service is the main entry point for the system. The web application is the main interface for the user to interact with the system. The user service is responsible for managing users. The authorization service is responsible for managing user permissions. The question service is responsible for generating questions. The mongo database is used to store data. From e14d9812c7b5a5a257f1e766c7b3ae3ca1a780bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fern=C3=A1ndez=20Noriega?= Date: Fri, 26 Apr 2024 14:25:29 +0200 Subject: [PATCH 06/14] Added CI/CD and our developing scheme to Cross cutting concepts --- docs/src/08_concepts.adoc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/src/08_concepts.adoc b/docs/src/08_concepts.adoc index 09a5d54..8ab2647 100644 --- a/docs/src/08_concepts.adoc +++ b/docs/src/08_concepts.adoc @@ -55,6 +55,15 @@ image::08-Crosscutting-Concepts-Structure-EN.png["Possible topics for crosscutti See https://docs.arc42.org/section-8/[Concepts] in the arc42 documentation. **** +=== _Continuous integration and development_ + +Our way of working with github is having one developing branch towards which all pull requests are done +and have to be reviewed by at least one team member. +Sonar Cloud is set up so we know the testing coverage and whether we pass the quality gate with every pull request. +Once there is enough change in the develop branch with respect to the main one and it has enough quality we can +merge them and make a release. + + === _Microservice based system_ Different business functionallities will be developed in different independent services. From e3b39c04e58a5efabfd59761be2ea73fbdcc08ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Cadenas?= <77900120+andrrsin@users.noreply.github.com> Date: Sat, 27 Apr 2024 18:04:05 +0200 Subject: [PATCH 07/14] http deploy --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index f6b35a0..3372b4a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "type": "chrome", "request": "launch", "name": "Launch Chrome against localhost", - "url": "http://localhost:3000", + "url": "http://localhost:80", "webRoot": "${workspaceFolder}" } ] From 0c44e16663f5e71375c593d3c466d0a103add114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Cadenas?= <77900120+andrrsin@users.noreply.github.com> Date: Sat, 27 Apr 2024 18:14:52 +0200 Subject: [PATCH 08/14] deploy fix --- .vscode/launch.json | 2 +- docker-compose.yml | 2 +- webapp/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 3372b4a..f6b35a0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "type": "chrome", "request": "launch", "name": "Launch Chrome against localhost", - "url": "http://localhost:80", + "url": "http://localhost:3000", "webRoot": "${workspaceFolder}" } ] diff --git a/docker-compose.yml b/docker-compose.yml index 82d4331..0a64eff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -85,7 +85,7 @@ services: depends_on: - gatewayservice ports: - - "80:80" + - "3000:3000" # platform: linux/arm64 diff --git a/webapp/package.json b/webapp/package.json index be7abce..40eef9d 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -24,7 +24,7 @@ "scripts": { "start": "react-scripts start", "build": "react-scripts build ", - "prod": "serve -s build -p 80", + "prod": "serve -s build", "test": "react-scripts test --transformIgnorePatterns 'node_modules/(?!axios)/'", "test:e2e": "start-server-and-test 'node e2e/test-environment-setup.js' http://localhost:8000/health prod 3000 \"cd e2e && jest\"", "eject": "react-scripts eject" From 40616ead94dbc19964a0ebff27dad9341cfa51da Mon Sep 17 00:00:00 2001 From: uo287841 Date: Sat, 27 Apr 2024 18:46:03 +0200 Subject: [PATCH 09/14] e2e login and add user --- users/authservice/auth-service.js | 1 + users/userservice/user-service.js | 2 + webapp/e2e/features/login-form.feature | 8 +- webapp/e2e/steps/login-form.steps.js | 97 +++++++++++-------------- webapp/e2e/steps/register-form.steps.js | 42 ++++------- webapp/e2e/test-environment-setup.js | 21 ++++++ 6 files changed, 83 insertions(+), 88 deletions(-) diff --git a/users/authservice/auth-service.js b/users/authservice/auth-service.js index 73b9a4d..b4549e1 100644 --- a/users/authservice/auth-service.js +++ b/users/authservice/auth-service.js @@ -12,6 +12,7 @@ app.use(express.json()); // Connect to MongoDB const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/wiq-en1a-users'; + mongoose.connect(mongoUri); // Function to validate required fields in the request body diff --git a/users/userservice/user-service.js b/users/userservice/user-service.js index a8989c6..b86eb56 100644 --- a/users/userservice/user-service.js +++ b/users/userservice/user-service.js @@ -15,6 +15,7 @@ app.use(bodyParser.json()); // Connect to MongoDB const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/wiq-en1a-users'; mongoose.connect(mongoUri); +console.log(mongoUri); @@ -127,6 +128,7 @@ app.get('/ranking/user', async (req, res) => { app.post('/adduser', async (req, res) => { try { + console.log(mongoUri); // Check if required fields are present in the request body validateRequiredFields(req, ['username','email', 'password']); diff --git a/webapp/e2e/features/login-form.feature b/webapp/e2e/features/login-form.feature index 0060f6f..a086ff7 100644 --- a/webapp/e2e/features/login-form.feature +++ b/webapp/e2e/features/login-form.feature @@ -1,6 +1,6 @@ -Feature: Registering a new user +Feature: Login a registered user Scenario: The user is registered in the site - Given A registered user - When I fill the data in the form and press submit - Then is logged \ No newline at end of file + Given An registered user + When I fill the data in the form to log in + Then is taken to the home page \ No newline at end of file diff --git a/webapp/e2e/steps/login-form.steps.js b/webapp/e2e/steps/login-form.steps.js index 0f30b9f..e949b11 100644 --- a/webapp/e2e/steps/login-form.steps.js +++ b/webapp/e2e/steps/login-form.steps.js @@ -3,69 +3,54 @@ const { defineFeature, loadFeature }=require('jest-cucumber'); const setDefaultOptions = require('expect-puppeteer').setDefaultOptions const feature = loadFeature('./features/login-form.feature'); -const axios = require('axios'); -const MockAdapter = require('axios-mock-adapter'); -const mockAxios = new MockAdapter(axios); let page; let browser; defineFeature(feature, test => { - beforeAll(async () => { - browser = process.env.GITHUB_ACTIONS - ? await puppeteer.launch() - : await puppeteer.launch({ headless: false, slowMo:40 }); - page = await browser.newPage(); - //Way of setting up the timeout - setDefaultOptions({ timeout: 10000 }) - - await page - .goto("http://localhost:3000", { - waitUntil: "networkidle0", - }) - .catch(() => {}); - - - }); - - beforeEach(async () => { - // Reset any state or actions before each test - await page.reload({ waitUntil: 'networkidle0' }); + beforeAll(async () => { + + browser = process.env.GITHUB_ACTIONS + ? await puppeteer.launch() + : await puppeteer.launch({ headless: false, slowMo: 30 }); + page = await browser.newPage(); + //Way of setting up the timeout + setDefaultOptions({ timeout: 10000 }) + + await page + .goto("http://localhost:3000/login", { + waitUntil: "networkidle0", + }) + .catch(() => {}); + }); + + test('The user is registered in the site', ({given,when,then}) => { + + let username; + let password; + + given('An registered user', async () => { + username = "testUser" + password = "testpass"; + }); - - test('The user is registered in the site', ({given,when,then}) => { - - let username; - let password; - let email - - given('A registered user', async () => { - username = "t1" - password = "t1pass" - - await expect(page).toClick("button", { text: "Create account" }); - }); - - when('I fill the data in the form and press submit', async () => { - await expect(page).toClick('a', { text: 'Already have an account? Log in here.' }); + when('I fill the data in the form to log in', async () => { await expect(page).toFill('input[name="username"]', username); await expect(page).toFill('input[name="password"]', password); - - mockAxios.onPost('http://localhost:8000/login').reply(200, { username:"t1",email:"t1email",createdAt: '2024-01-01T12:34:56Z',token: 'testToken'}); - - - await expect(page).toClick('button', { text: 'Login' }) - }); - - then('is logged', async () => { - await expect(page).toMatchElement("div", { text: "Welcome back, " + username + "!" }); - }); - }) - - afterAll(async ()=>{ - browser.close() - }) - - }); \ No newline at end of file + await expect(page).toClick("button", { text: "Log In" }); + }); + + then('is taken to the home page', async () => { + await page.waitForNavigation({ waitUntil: "networkidle0" }); + await expect(page).toMatchElement("h1", { text: "Welcome back, " + username + "!" }); + }); + + }); + + afterAll(async ()=>{ + browser.close() + }) + +}); \ No newline at end of file diff --git a/webapp/e2e/steps/register-form.steps.js b/webapp/e2e/steps/register-form.steps.js index ad9e64f..7e87115 100644 --- a/webapp/e2e/steps/register-form.steps.js +++ b/webapp/e2e/steps/register-form.steps.js @@ -3,9 +3,6 @@ const { defineFeature, loadFeature }=require('jest-cucumber'); const setDefaultOptions = require('expect-puppeteer').setDefaultOptions const feature = loadFeature('./features/register-form.feature'); -const axios = require('axios'); -const MockAdapter = require('axios-mock-adapter'); -const mockAxios = new MockAdapter(axios); let page; let browser; @@ -13,58 +10,47 @@ let browser; defineFeature(feature, test => { beforeAll(async () => { + browser = process.env.GITHUB_ACTIONS ? await puppeteer.launch() - : await puppeteer.launch({ headless: false, slowMo:60 }); + : await puppeteer.launch({ headless: false, slowMo: 30 }); page = await browser.newPage(); //Way of setting up the timeout setDefaultOptions({ timeout: 10000 }) await page - .goto("http://localhost:3000", { + .goto("http://localhost:3000/login", { waitUntil: "networkidle0", }) .catch(() => {}); - - - }); - - beforeEach(async () => { - // Reset any state or actions before each test - await page.reload({ waitUntil: 'networkidle0' }); }); test('The user is not registered in the site', ({given,when,then}) => { let username; let password; - let email given('An unregistered user', async () => { - username = "t1" - email = "t1email" - password = "t1pass" - - await expect(page).toClick("button", { text: "Create account" }); + username = "newUser" + password = "newUser" + await expect(page).toClick("a", { text: "Create account" }); }); when('I fill the data in the form and press submit', async () => { + await expect(page).toFill('input[name="username"]', username); - await expect(page).toFill('input[name="email"]', email); + await expect(page).toFill('input[name="email"]', username + "@" + "gmail.com"); await expect(page).toFill('input[name="password"]', password); - await expect(page).toFill('input[name="cpassword"]', password); - - // mockAxios.onPost('http://localhost:8000/adduser').reply(200, { username: "t1", email: "t1email", password: "t1pass" }); - mockAxios.onPost('http://localhost:8000/adduser').reply(200, { username:"t1",email:"t1email",password: 't1pass'}); - - - await expect(page).toClick('button', { text: 'Register' }) + await expect(page).toFill('input[name="cpassword"]', password); + await expect(page).toClick("button", { text: "Register" }); }); then('is taken to login', async () => { - //await expect(page).toMatchElement("div", { text: "Login" }); + await page.waitForNavigation({ waitUntil: "networkidle0" }); + await expect(page).toMatchElement("h1", { text: "Access WIQ" }); }); - }) + + }); afterAll(async ()=>{ browser.close() diff --git a/webapp/e2e/test-environment-setup.js b/webapp/e2e/test-environment-setup.js index 7b7ed51..9c14242 100644 --- a/webapp/e2e/test-environment-setup.js +++ b/webapp/e2e/test-environment-setup.js @@ -1,4 +1,6 @@ const { MongoMemoryServer } = require('mongodb-memory-server'); +const User = require('../../users/userservice/user-model'); +const axios = require('axios'); let mongoserver; @@ -9,11 +11,30 @@ let gatewayservice; async function startServer() { console.log('Starting MongoDB memory server...'); mongoserver = await MongoMemoryServer.create(); + const mongoUri = mongoserver.getUri(); process.env.MONGODB_URI = mongoUri; userservice = await require("../../users/userservice/user-service"); authservice = await require("../../users/authservice/auth-service"); gatewayservice = await require("../../gatewayservice/gateway-service"); + + // Add test user + await addUser('testUser', 'test@email', 'testpass'); + } + async function addUser(username, email, password) { + try { + const response = await axios.post('http://localhost:8001/adduser', { + username: username, + email: email, + password: password + }); + } catch (error) { + console.error('Error adding user:', error.response.data); + } +} + + + startServer(); From e85433a730625a83a16fb0423d323bc84c83acb9 Mon Sep 17 00:00:00 2001 From: uo287841 Date: Sat, 27 Apr 2024 21:50:22 +0200 Subject: [PATCH 10/14] user progule e2e --- webapp/e2e/features/login-form.feature | 12 ++- webapp/e2e/features/userprofile-form.feature | 17 +++ webapp/e2e/steps/login-form.steps.js | 35 ++++++ webapp/e2e/steps/userprofile-form.steps.js | 107 +++++++++++++++++++ webapp/e2e/test-environment-setup.js | 7 ++ 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 webapp/e2e/features/userprofile-form.feature create mode 100644 webapp/e2e/steps/userprofile-form.steps.js diff --git a/webapp/e2e/features/login-form.feature b/webapp/e2e/features/login-form.feature index a086ff7..d8bdf51 100644 --- a/webapp/e2e/features/login-form.feature +++ b/webapp/e2e/features/login-form.feature @@ -3,4 +3,14 @@ Feature: Login a registered user Scenario: The user is registered in the site Given An registered user When I fill the data in the form to log in - Then is taken to the home page \ No newline at end of file + Then is taken to the home page + +Scenario: User logs in with invalid credentials + Given a registered user with username "testUser" and password "testpass" + When I fill the login form with username "testUser" and incorrect password "wrongpass" + And I remain on the login page + +Scenario: User attempts to login without entering credentials + Given a registered user with username "testUser" and password "testpass" + When I attempt to log in without entering any credentials + And I remain on the login page \ No newline at end of file diff --git a/webapp/e2e/features/userprofile-form.feature b/webapp/e2e/features/userprofile-form.feature new file mode 100644 index 0000000..467d758 --- /dev/null +++ b/webapp/e2e/features/userprofile-form.feature @@ -0,0 +1,17 @@ +Feature: View and Change User Quiz Rankings + +Scenario: Viewing Global Rankings + Given the user navigates to their profile + When they select the "Global" category + Then they see their performance statistics for global quizzes + +Scenario: Switching Category to Flags + Given the user is on their profile page + When they click on the "Flags" category + Then they view their performance metrics for flag-related quizzes + +Scenario: Switching Category to Food + Given the user is on their profile page + When they click on the "Food" category + Then they view their performance metrics for food-related quizzes + diff --git a/webapp/e2e/steps/login-form.steps.js b/webapp/e2e/steps/login-form.steps.js index e949b11..239389b 100644 --- a/webapp/e2e/steps/login-form.steps.js +++ b/webapp/e2e/steps/login-form.steps.js @@ -45,10 +45,45 @@ defineFeature(feature, test => { then('is taken to the home page', async () => { await page.waitForNavigation({ waitUntil: "networkidle0" }); await expect(page).toMatchElement("h1", { text: "Welcome back, " + username + "!" }); + await expect(page).toClick("button", { text: "Log out" }); }); }); + test('User logs in with invalid credentials', ({ given, when, then }) => { + given('a registered user with username "testUser" and password "testpass"', async () => { + // No specific action needed since the user is already registered + }); + + when('I fill the login form with username "testUser" and incorrect password "wrongpass"', async () => { + await expect(page).toFill('input[name="username"]', 'testUser'); + await expect(page).toFill('input[name="password"]', 'wrongpass'); + await expect(page).toClick("button", { text: "Log In" }); + }); + + + then('I remain on the login page', async () => { + await expect(page).toMatchElement("h1", { text: "Access WIQ" }); + }); + }); + + test('User attempts to login without entering credentials', ({ given, when, then }) => { + given('a registered user with username "testUser" and password "testpass"', async () => { + // No specific action needed since the user is already registered + }); + + when('I attempt to log in without entering any credentials', async () => { + await expect(page).toFill('input[name="username"]', ''); + await expect(page).toFill('input[name="password"]', ''); + await expect(page).toClick("button", { text: "Log In" }); + }); + + + then('I remain on the login page', async () => { + await expect(page).toMatchElement("h1", { text: "Access WIQ" }); + }); + }); + afterAll(async ()=>{ browser.close() }) diff --git a/webapp/e2e/steps/userprofile-form.steps.js b/webapp/e2e/steps/userprofile-form.steps.js new file mode 100644 index 0000000..6232c9c --- /dev/null +++ b/webapp/e2e/steps/userprofile-form.steps.js @@ -0,0 +1,107 @@ +const puppeteer = require('puppeteer'); +const { defineFeature, loadFeature }=require('jest-cucumber'); +const setDefaultOptions = require('expect-puppeteer').setDefaultOptions +const feature = loadFeature('./features/userprofile-form.feature'); + + +let page; +let browser; + +defineFeature(feature, test => { + + beforeAll(async () => { + + browser = process.env.GITHUB_ACTIONS + ? await puppeteer.launch() + : await puppeteer.launch({ headless: false, slowMo: 20 }); + page = await browser.newPage(); + //Way of setting up the timeout + setDefaultOptions({ timeout: 10000 }) + + await page + .goto("http://localhost:3000/login", { + waitUntil: "networkidle0", + }) + .catch(() => {}); + }); + + test('Viewing Global Rankings', ({given,when,then}) => { + + let username; + let password; + + given('the user navigates to their profile', async () => { + username = "testUser" + password = "testpass"; + await expect(page).toClick("a", { text: "Log In" }); + await expect(page).toFill('input[name="username"]', username); + await expect(page).toFill('input[name="password"]', password); + await expect(page).toClick("button", { text: "Log In" }); + await expect(page).toClick("button", { text: "My stats" }); + }); + + when('they select the "Global" category', async () => { + + await expect(page).toMatchElement("h2", { text: "Username: " + username }); + await expect(page).toClick("button", { text: "Flags" }); + await expect(page).toClick("button", { text: "Global" }); + }); + + then('they see their performance statistics for global quizzes', async () => { + await expect(page).toMatchElement('.ranking h3', { text: "global Ranking" }); + await expect(page).toMatchElement(".ranking p:nth-child(1)", { text: "Total Answered Questions: " + 1 }); + }); + + }); + + test('Switching Category to Flags', ({given,when,then}) => { + + let username; + let password; + + given('the user is on their profile page', async () => { + username = "testUser" + password = "testpass"; + await expect(page).toMatchElement("h2", { text: "Username: " + username }); + }); + + when('they click on the "Flags" category', async () => { + + await expect(page).toClick("button", { text: "Flags" }); + }); + + then('they view their performance metrics for flag-related quizzes', async () => { + await expect(page).toMatchElement('.ranking h3', { text: "flags Ranking" }); + await expect(page).toMatchElement(".ranking p:nth-child(1)", { text: "Total Answered Questions: " + 1 }); + }); + + }); + + test('Switching Category to Food', ({given,when,then}) => { + + let username; + let password; + + given('the user is on their profile page', async () => { + username = "testUser" + password = "testpass"; + await expect(page).toMatchElement("h2", { text: "Username: " + username }); + }); + + when('they click on the "Food" category', async () => { + + await expect(page).toClick("button", { text: "Food" }); + }); + + then('they view their performance metrics for food-related quizzes', async () => { + await expect(page).toMatchElement('.ranking h3', { text: "foods Ranking" }); + await expect(page).toMatchElement(".ranking p:nth-child(1)", { text: "Total Answered Questions: " + 0 }); + }); + + }); + + afterAll(async ()=>{ + browser.close() + }) + +}); \ No newline at end of file diff --git a/webapp/e2e/test-environment-setup.js b/webapp/e2e/test-environment-setup.js index 9c14242..8a43a0f 100644 --- a/webapp/e2e/test-environment-setup.js +++ b/webapp/e2e/test-environment-setup.js @@ -30,6 +30,13 @@ async function startServer() { email: email, password: password }); + + const pints = await axios.post('http://localhost:8001/addpoints', { + username: username, + category: "flags", + correct: "true" + }); + } catch (error) { console.error('Error adding user:', error.response.data); } From 51b141ff42b576cdcc73ae70916f2500b09f9e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Cadenas?= <77900120+andrrsin@users.noreply.github.com> Date: Sat, 27 Apr 2024 22:14:03 +0200 Subject: [PATCH 11/14] update of navbar --- webapp/package-lock.json | 405 +++++++++++++++++++++++++++ webapp/package.json | 1 + webapp/src/components/Navbar.jsx | 97 ++++--- webapp/src/components/Navbar.test.js | 14 +- 4 files changed, 479 insertions(+), 38 deletions(-) diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 337894f..6e23848 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", + "@material-tailwind/react": "^2.1.9", "@mui/icons-material": "^5.15.15", "@mui/material": "^5.15.15", "@testing-library/jest-dom": "^5.17.0", @@ -2588,6 +2589,20 @@ "@floating-ui/utils": "^0.2.0" } }, + "node_modules/@floating-ui/react": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.19.0.tgz", + "integrity": "sha512-fgYvN4ksCi5OvmPXkyOT8o5a8PSKHMzPHt+9mR6KYWdF16IAjWRLZPAAziI2sznaWT23drRFrYw64wdvYqqaQw==", + "dependencies": { + "@floating-ui/react-dom": "^1.2.2", + "aria-hidden": "^1.1.3", + "tabbable": "^6.0.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@floating-ui/react-dom": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", @@ -2600,6 +2615,18 @@ "react-dom": ">=16.8.0" } }, + "node_modules/@floating-ui/react/node_modules/@floating-ui/react-dom": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.3.0.tgz", + "integrity": "sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==", + "dependencies": { + "@floating-ui/dom": "^1.2.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@floating-ui/utils": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", @@ -4518,6 +4545,34 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@material-tailwind/react": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@material-tailwind/react/-/react-2.1.9.tgz", + "integrity": "sha512-3uPlJE9yK4JF9DEQO4I1QbjR8o05+4fysLqoZ0v38TDOLE2tvDRhTBVhn6Mp9vSsq5CoJOKgemG7kbkOFAji4A==", + "dependencies": { + "@floating-ui/react": "0.19.0", + "classnames": "2.3.2", + "deepmerge": "4.2.2", + "framer-motion": "6.5.1", + "material-ripple-effects": "2.0.1", + "prop-types": "15.8.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "tailwind-merge": "1.8.1" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, + "node_modules/@material-tailwind/react/node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.3.tgz", @@ -4528,6 +4583,64 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@motionone/animation": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.17.0.tgz", + "integrity": "sha512-ANfIN9+iq1kGgsZxs+Nz96uiNcPLGTXwfNo2Xz/fcJXniPYpaz/Uyrfa+7I5BPLxCP82sh7quVDudf1GABqHbg==", + "dependencies": { + "@motionone/easing": "^10.17.0", + "@motionone/types": "^10.17.0", + "@motionone/utils": "^10.17.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/dom": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", + "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", + "dependencies": { + "@motionone/animation": "^10.12.0", + "@motionone/generators": "^10.12.0", + "@motionone/types": "^10.12.0", + "@motionone/utils": "^10.12.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/easing": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.17.0.tgz", + "integrity": "sha512-Bxe2wSuLu/qxqW4rBFS5m9tMLOw+QBh8v5A7Z5k4Ul4sTj5jAOfZG5R0bn5ywmk+Fs92Ij1feZ5pmC4TeXA8Tg==", + "dependencies": { + "@motionone/utils": "^10.17.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/generators": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.17.0.tgz", + "integrity": "sha512-T6Uo5bDHrZWhIfxG/2Aut7qyWQyJIWehk6OB4qNvr/jwA/SRmixwbd7SOrxZi1z5rH3LIeFFBKK1xHnSbGPZSQ==", + "dependencies": { + "@motionone/types": "^10.17.0", + "@motionone/utils": "^10.17.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/types": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.0.tgz", + "integrity": "sha512-EgeeqOZVdRUTEHq95Z3t8Rsirc7chN5xFAPMYFobx8TPubkEfRSm5xihmMUkbaR2ErKJTUw3347QDPTHIW12IA==" + }, + "node_modules/@motionone/utils": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.17.0.tgz", + "integrity": "sha512-bGwrki4896apMWIj9yp5rAS2m0xyhxblg6gTB/leWDPt+pb410W8lYWsxyurX+DH+gO1zsQsfx2su/c1/LtTpg==", + "dependencies": { + "@motionone/types": "^10.17.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, "node_modules/@mui/base": { "version": "5.0.0-beta.40", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", @@ -6861,6 +6974,17 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -8303,6 +8427,11 @@ "node": ">= 0.4" } }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -11652,6 +11781,49 @@ "node": ">=0.10.0" } }, + "node_modules/framer-motion": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", + "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", + "dependencies": { + "@motionone/dom": "10.12.0", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": ">=16.8 || ^17.0.0 || ^18.0.0", + "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/framer-motion/node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/framer-motion/node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, + "node_modules/framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -12197,6 +12369,11 @@ "he": "bin/he" } }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -18924,6 +19101,11 @@ "node": ">=0.10.0" } }, + "node_modules/material-ripple-effects": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/material-ripple-effects/-/material-ripple-effects-2.0.1.tgz", + "integrity": "sha512-hHlUkZAuXbP94lu02VgrPidbZ3hBtgXBtjlwR8APNqOIgDZMV8MCIcsclL8FmGJQHvnORyvoQgC965vPsiyXLQ==" + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -20441,6 +20623,17 @@ "node": ">=4" } }, + "node_modules/popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -25835,6 +26028,15 @@ "webpack": "^5.0.0" } }, + "node_modules/style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -26093,6 +26295,16 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, + "node_modules/tailwind-merge": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.8.1.tgz", + "integrity": "sha512-+fflfPxvHFr81hTJpQ3MIwtqgvefHZFUHFiIHpVIRXvG/nX9+gu2P7JNlFu2bfDMJ+uHhi/pUgzaYacMoXv+Ww==" + }, "node_modules/tailwindcss": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", @@ -29794,6 +30006,26 @@ "@floating-ui/utils": "^0.2.0" } }, + "@floating-ui/react": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.19.0.tgz", + "integrity": "sha512-fgYvN4ksCi5OvmPXkyOT8o5a8PSKHMzPHt+9mR6KYWdF16IAjWRLZPAAziI2sznaWT23drRFrYw64wdvYqqaQw==", + "requires": { + "@floating-ui/react-dom": "^1.2.2", + "aria-hidden": "^1.1.3", + "tabbable": "^6.0.1" + }, + "dependencies": { + "@floating-ui/react-dom": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.3.0.tgz", + "integrity": "sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==", + "requires": { + "@floating-ui/dom": "^1.2.1" + } + } + } + }, "@floating-ui/react-dom": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", @@ -31278,6 +31510,29 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "@material-tailwind/react": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@material-tailwind/react/-/react-2.1.9.tgz", + "integrity": "sha512-3uPlJE9yK4JF9DEQO4I1QbjR8o05+4fysLqoZ0v38TDOLE2tvDRhTBVhn6Mp9vSsq5CoJOKgemG7kbkOFAji4A==", + "requires": { + "@floating-ui/react": "0.19.0", + "classnames": "2.3.2", + "deepmerge": "4.2.2", + "framer-motion": "6.5.1", + "material-ripple-effects": "2.0.1", + "prop-types": "15.8.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "tailwind-merge": "1.8.1" + }, + "dependencies": { + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + } + } + }, "@mongodb-js/saslprep": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.3.tgz", @@ -31288,6 +31543,64 @@ "sparse-bitfield": "^3.0.3" } }, + "@motionone/animation": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.17.0.tgz", + "integrity": "sha512-ANfIN9+iq1kGgsZxs+Nz96uiNcPLGTXwfNo2Xz/fcJXniPYpaz/Uyrfa+7I5BPLxCP82sh7quVDudf1GABqHbg==", + "requires": { + "@motionone/easing": "^10.17.0", + "@motionone/types": "^10.17.0", + "@motionone/utils": "^10.17.0", + "tslib": "^2.3.1" + } + }, + "@motionone/dom": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", + "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", + "requires": { + "@motionone/animation": "^10.12.0", + "@motionone/generators": "^10.12.0", + "@motionone/types": "^10.12.0", + "@motionone/utils": "^10.12.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "@motionone/easing": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.17.0.tgz", + "integrity": "sha512-Bxe2wSuLu/qxqW4rBFS5m9tMLOw+QBh8v5A7Z5k4Ul4sTj5jAOfZG5R0bn5ywmk+Fs92Ij1feZ5pmC4TeXA8Tg==", + "requires": { + "@motionone/utils": "^10.17.0", + "tslib": "^2.3.1" + } + }, + "@motionone/generators": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.17.0.tgz", + "integrity": "sha512-T6Uo5bDHrZWhIfxG/2Aut7qyWQyJIWehk6OB4qNvr/jwA/SRmixwbd7SOrxZi1z5rH3LIeFFBKK1xHnSbGPZSQ==", + "requires": { + "@motionone/types": "^10.17.0", + "@motionone/utils": "^10.17.0", + "tslib": "^2.3.1" + } + }, + "@motionone/types": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.0.tgz", + "integrity": "sha512-EgeeqOZVdRUTEHq95Z3t8Rsirc7chN5xFAPMYFobx8TPubkEfRSm5xihmMUkbaR2ErKJTUw3347QDPTHIW12IA==" + }, + "@motionone/utils": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.17.0.tgz", + "integrity": "sha512-bGwrki4896apMWIj9yp5rAS2m0xyhxblg6gTB/leWDPt+pb410W8lYWsxyurX+DH+gO1zsQsfx2su/c1/LtTpg==", + "requires": { + "@motionone/types": "^10.17.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, "@mui/base": { "version": "5.0.0-beta.40", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", @@ -32956,6 +33269,14 @@ "sprintf-js": "~1.0.2" } }, + "aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "requires": { + "tslib": "^2.0.0" + } + }, "aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -33975,6 +34296,11 @@ } } }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -36459,6 +36785,45 @@ "map-cache": "^0.2.2" } }, + "framer-motion": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", + "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", + "requires": { + "@emotion/is-prop-valid": "^0.8.2", + "@motionone/dom": "10.12.0", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "requires": { + "@emotion/memoize": "0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + } + } + }, + "framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "requires": { + "tslib": "^2.1.0" + } + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -36856,6 +37221,11 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -41972,6 +42342,11 @@ "object-visit": "^1.0.0" } }, + "material-ripple-effects": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/material-ripple-effects/-/material-ripple-effects-2.0.1.tgz", + "integrity": "sha512-hHlUkZAuXbP94lu02VgrPidbZ3hBtgXBtjlwR8APNqOIgDZMV8MCIcsclL8FmGJQHvnORyvoQgC965vPsiyXLQ==" + }, "mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -43073,6 +43448,17 @@ } } }, + "popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "requires": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -46946,6 +47332,15 @@ "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==", "requires": {} }, + "style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "requires": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -47136,6 +47531,16 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, + "tailwind-merge": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.8.1.tgz", + "integrity": "sha512-+fflfPxvHFr81hTJpQ3MIwtqgvefHZFUHFiIHpVIRXvG/nX9+gu2P7JNlFu2bfDMJ+uHhi/pUgzaYacMoXv+Ww==" + }, "tailwindcss": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", diff --git a/webapp/package.json b/webapp/package.json index 40eef9d..5c0587c 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -5,6 +5,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", + "@material-tailwind/react": "^2.1.9", "@mui/icons-material": "^5.15.15", "@mui/material": "^5.15.15", "@testing-library/jest-dom": "^5.17.0", diff --git a/webapp/src/components/Navbar.jsx b/webapp/src/components/Navbar.jsx index b32d636..49c1c27 100644 --- a/webapp/src/components/Navbar.jsx +++ b/webapp/src/components/Navbar.jsx @@ -2,7 +2,14 @@ import { Link } from 'react-router-dom'; import useIsAuthenticated from 'react-auth-kit/hooks/useIsAuthenticated'; import useSignOut from 'react-auth-kit/hooks/useSignOut'; import { useNavigate } from 'react-router-dom'; -function Navbar() { +import React from 'react' +import { + Collapse, + IconButton, +} from "@material-tailwind/react"; +import MenuRoundedIcon from '@mui/icons-material/MenuRounded'; +import ClearRoundedIcon from '@mui/icons-material/ClearRounded'; +function NavbarDefault() { const isAuthenticated = useIsAuthenticated(); const signOut = useSignOut(); const navigate = useNavigate(); @@ -11,44 +18,68 @@ function Navbar() { navigate('/login'); } + const [openNav, setOpenNav] = React.useState(false); + + React.useEffect(() => { + window.addEventListener( + "resize", + () => window.innerWidth >= 960 && setOpenNav(false), + ); + }, []); + const navList = ( + // + + ); + return ( -