Update README

This commit is contained in:
AndrewTrieu
2023-03-12 20:19:04 +02:00
parent 6a3e4d8cd9
commit 346fcd3e3d
5 changed files with 208 additions and 8 deletions

View File

102
README.md
View File

@@ -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 <https://quizzy.fly.dev>.
## 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 <https://quizzy.fly.dev/api/questions/random>. 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
```

View File

@@ -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");
});

View File

@@ -2,7 +2,7 @@
<h1>Quizzy Application</h1>
<P>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.</p>
<p>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.</p>
<p>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.</p>

View File

@@ -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");
});
});
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.");
});