From 346fcd3e3d71e8f397b2dbc2b75b8cf0fec41f1a Mon Sep 17 00:00:00 2001 From: AndrewTrieu Date: Sun, 12 Mar 2023 20:19:04 +0200 Subject: [PATCH] Update README --- .Rhistory | 0 README.md | 102 +++++++++++++++++++++-- drill-and-practice/tests/app_test.js | 12 +++ drill-and-practice/views/main.eta | 2 +- e2e-playwright/tests/hello-world.spec.js | 100 +++++++++++++++++++++- 5 files changed, 208 insertions(+), 8 deletions(-) delete mode 100644 .Rhistory diff --git a/.Rhistory b/.Rhistory deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md index a48dbcf..73f92e1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,99 @@ -# Project 2: XXX +# Quizzy Application -Write the documentation of your project here. Do not include your personal -details (e.g. name or student number). +## Overview -Remember to include the address of the online location where your project is -running as it is a key part of the submission. +This is a web application that is used for repeated practice of learned content. The application provides a list of topics and allows creating multiple-choice questions into those topics that are then answered by self and others. The application also shows basic statistics: the total number of available questions and the total number of question answers. In addition, the application also provides an API for retrieving and answering random questions. + +PostgreSQL is used as a database for storing topics, questions, and answers. The database is managed using Flyway. The application is written in JavaScript using the Deno runtime. The application is deployed using Fly.io and can be accessed at . + +## Usage + +To access the functionality of the application, you need to create an account. After that, you can create topics and questions. The application provides a list of topics and allows creating multiple-choice questions into those topics that are then answered by self and others. Start answering questions by clicking on the "Quiz" button on the top right corner of the page. + +## API + +The application provides an API for retrieving and answering random questions. The API is available at . To verify your answer, you need to provide the question ID and the answer ID in the request body, for example: + +```bash +curl -X POST -H "Content-Type: application/json" -d '{"questionId": 1, "answerId": 1}' https://quizzy.fly.dev/api/questions/answer +``` + +The API will return a JSON object with `correct`: a boolean value indicating whether the answer is correct or not. + +## File Structure + +```bash +. +├── docker-compose.yml +├── drill-and-practice +│ ├── app.js +│ ├── app-launch.js +│ ├── database +│ │ └── database.js +│ ├── deps.js +│ ├── Dockerfile +│ ├── fly.toml +│ ├── middlewares +│ │ ├── authMiddleware.js +│ │ ├── errorMiddleware.js +│ │ ├── renderMiddleware.js +│ │ ├── serveStaticMiddleware.js +│ │ └── userMiddleware.js +│ ├── routes +│ │ ├── apis +│ │ │ └── questionApi.js +│ │ ├── controllers +│ │ │ ├── answerController.js +│ │ │ ├── authController.js +│ │ │ ├── mainController.js +│ │ │ ├── questionController.js +│ │ │ └── topicController.js +│ │ └── routes.js +│ ├── services +│ │ ├── answerService.js +│ │ ├── authService.js +│ │ ├── questionService.js +│ │ └── topicService.js +│ ├── tests +│ │ └── app_test.js +│ └── views +│ ├── correct.eta +│ ├── incorrect.eta +│ ├── layouts +│ │ └── layout.eta +│ ├── login.eta +│ ├── main.eta +│ ├── question.eta +│ ├── questions.eta +│ ├── quiz.eta +│ ├── quizTopics.eta +│ ├── register.eta +│ └── topics.eta +├── e2e-playwright +│ ├── Dockerfile +│ ├── package.json +│ ├── playwright.config.js +│ └── tests +│ └── hello-world.spec.js +├── flyway +│ └── sql +│ └── V1___initial_schema.sql +├── project.env +└── README.md +``` + +## Testing + +The application is tested using Playwright. The tests are located in the `e2e-playwright` directory. To run the tests, you need to have Docker installed. Then, run the following command: + +```bash +docker-compose run --entrypoint=npx e2e-playwright playwright test && docker-compose rm -sf +``` + +## Local Development + +To run the application locally, you need to have Docker installed. Then, run the following command: + +```bash +docker-compose up +``` diff --git a/drill-and-practice/tests/app_test.js b/drill-and-practice/tests/app_test.js index e69de29..464e79a 100644 --- a/drill-and-practice/tests/app_test.js +++ b/drill-and-practice/tests/app_test.js @@ -0,0 +1,12 @@ +import { app } from "../app.js"; +import { superoak } from "https://deno.land/x/superoak@4.4.0/mod.ts"; + +Deno.test("Home page works", async () => { + const testClient = await superoak(app); + const request = testClient + .get("https://quizzy.fly.dev") + .set("Origin", "http://localhost:8000"); + await request + .expect(new RegExp("Quizzy")) + .expect("Access-Control-Allow-Origin", "http://localhost:8000"); +}); diff --git a/drill-and-practice/views/main.eta b/drill-and-practice/views/main.eta index c0b7b9e..d78903d 100644 --- a/drill-and-practice/views/main.eta +++ b/drill-and-practice/views/main.eta @@ -2,7 +2,7 @@

Quizzy Application

-

This is a web application that is used for repeated practice of learned content. The application provides a list of topics and allows creating multiple-choice questions into those topics that are then answered by self and others.

+

This is a web application that is used for repeated practice of learned content. The application provides a list of topics and allows creating multiple-choice questions into those topics that are then answered by self and others.

The application also shows basic statistics: the total number of available questions and the total number of question answers. In addition, the application also provides an API for retrieving and answering random questions.

diff --git a/e2e-playwright/tests/hello-world.spec.js b/e2e-playwright/tests/hello-world.spec.js index 64e155f..d91161e 100644 --- a/e2e-playwright/tests/hello-world.spec.js +++ b/e2e-playwright/tests/hello-world.spec.js @@ -1,5 +1,101 @@ const { test, expect } = require("@playwright/test"); -test("Empty test", async ({ page }) => { +test("Main page is working", async ({ page }) => { + await page.goto("/"); + await expect(page.locator("h1")).toHaveText("Quizzy Application"); +}); -}); \ No newline at end of file +test("Main page has statistics", async ({ page }) => { + await page.goto("/"); + await expect(page.locator("h2")).toHaveText("Statistics"); +}); + +test("Login page is working", async ({ page }) => { + await page.goto("/auth/login"); + await expect(page.locator("h1")).toHaveText("Login"); +}); + +test("Registration page is working", async ({ page }) => { + await page.goto("/auth/register"); + await expect(page.locator("h1")).toHaveText("New user registration"); +}); + +test("Can logout", async ({ page }) => { + await page.goto("/logout"); + await expect(page.locator("h1")).toHaveText("Quizzy Application"); +}); + +test("Can login as admin", async ({ page }) => { + await page.goto("/auth/login"); + await page.locator("input[type=email]").type("admin@admin.com"); + await page.locator("input[type=password]").type("123456"); + await page.locator("input[type=submit]").click(); + await expect(page.locator("h1")).toHaveText("Topics"); +}); + +test("Can add a new topic", async ({ page }) => { + await page.goto("/auth/login"); + await page.locator("input[type=email]").type("admin@admin.com"); + await page.locator("input[type=password]").type("123456"); + await page.locator("input[type=submit]").click(); + await page.goto("/topics"); + await page.locator("input[type=text]").type("Test topic"); + await page.getByRole("button", { name: "Add" }).click(); + await expect(page.locator("h1")).toHaveText("Topics"); + await expect(page.getByRole("link", { name: "Test topic" })).toHaveText( + "Test topic" + ); +}); + +test("Can see added topic", async ({ page }) => { + await page.goto("/auth/login"); + await page.locator("input[type=email]").type("admin@admin.com"); + await page.locator("input[type=password]").type("123456"); + await page.locator("input[type=submit]").click(); + await page.goto("/topics"); + await expect(page.locator("h1")).toHaveText("Topics"); + await expect(page.getByRole("link", { name: "Test topic" })).toHaveText( + "Test topic" + ); +}); + +test("Can add a question to a topic", async ({ page }) => { + await page.goto("/auth/login"); + await page.locator("input[type=email]").type("admin@admin.com"); + await page.locator("input[type=password]").type("123456"); + await page.locator("input[type=submit]").click(); + await page.goto("/topics"); + await page.getByRole("link", { name: "Test topic" }).click(); + await page.locator("input[type=text]").type("Test question"); + await page.locator("input[type=submit]").click(); + await expect(page.getByRole("link", { name: "Test question" })).toHaveText( + "Test question" + ); +}); + +test("Can add an answer to a question", async ({ page }) => { + await page.goto("/auth/login"); + await page.locator("input[type=email]").type("admin@admin.com"); + await page.locator("input[type=password]").type("123456"); + await page.locator("input[type=submit]").click(); + await page.goto("/topics"); + await page.getByRole("link", { name: "Test topic" }).click(); + await page.getByRole("link", { name: "Test question" }).click(); + await page.locator("input[type=text]").type("Test answer"); + await page.getByRole("button", { name: "Add" }).click(); + console.log(await page.content()); + await expect( + page.getByText("Content: Test answer; Correctness: false Delete option") + ).toHaveText("Content: Test answer; Correctness: false Delete option"); +}); + +test("Can delete a topic", async ({ page }) => { + await page.goto("/auth/login"); + await page.locator("input[type=email]").type("admin@admin.com"); + await page.locator("input[type=password]").type("123456"); + await page.locator("input[type=submit]").click(); + await page.goto("/topics"); + await page.getByRole("button", { name: "Delete" }).click(); + await expect(page.locator("h1")).toHaveText("Topics"); + await expect(page.locator("p")).toHaveText("No available topics."); +});