The project target is helping hearing impaired students during the lessons they attend. But potentially can be used by any student.
The project aims to introduce a speech to text system able to transcript the voice of the professor into a text on the screen of the student pc or mobile phone.
Students can record teacher voice by clicking a button which will transcrypt voice to text for their better understandment.
Registered user will have premium feature to store the transcriptions online and save them to a file (txt or pdf format).
Students will be able to change speech language.
Students will be able to search lessons by title they provide before transcryption to text.
Server side of application will be made in Node
using Express
framework.
Data will be saved to Mongo
database.
Data will be cached in Redis
cache.
Tests will be written in Cypress
and Supertest
.
Client side will be created in React
using Tailwindcss
framework.
React
Tailwindcss
TypeScript
Express
MongoDB
Mongoose
Redis
Jest
Supertest
Cypress
- install
node
- install
mongo
- install
redis
git clone https://github.com/wojciechszmelczerczyk/express-notes-app.git
cd /mern-notes-app
npm i
npm run client
npm run server
npm run app
# Uri to mongo database
DB_URI=
# Speech key for Microsoft Cognitive Services
SPEECH_KEY=
# Speech region for Microsoft Cognitive Services
SPEECH_REGION=
# Port
PORT=
# Token for test purposes
JWT=
# Access token secret
ACCESS_TOKEN_SECRET=
# Refresh token secret
REFRESH_TOKEN_SECRET=
# Access token expiration time
ACCESS_TOKEN_EXP=
# Refresh token expiration time
REFRESH_TOKEN_EXP=
Endpoint | Authenticated | Component | Description |
---|---|---|---|
/ |
* | NoteListComponent | Note list of current auth user |
/register |
- | RegisterComponent | Register form |
/login |
- | LoginComponent | Login form |
/note/:id |
* | NoteDetailsComponent | Single note details, speech-to-text service |
/createNote |
* | CreateNoteComponent | Set note title |
Method | Endpoint |
---|---|
GET | /user |
POST | /user |
POST | /user/authenticate |
GET | /user/refresh-token |
Method | Endpoint |
---|---|
GET | /note |
POST | /note |
PUT | /note/:id |
GET | /note/:id |
DELETE | /note/:id |
POST | /note/:id/file |
Method | Endpoint |
---|---|
GET | /api/get-speech-token |
npm run e2e
if note title is too short, prompt an error
it("if note title is too short, prompt an error", () => {
localStorage.setItem("at", "");
cy.visit("http://localhost:5000/createNote");
cy.get('[data-cy="noteTitleInput"]').type("tes");
cy.get('[data-cy="createNoteButton"]').click();
cy.get('[data-cy="createNoteError"]').should(
"contain",
"Note title is too short."
);
});
if note title is too long, prompt an error
it("if note title is too long, prompt an error", () => {
localStorage.setItem("at", "");
cy.visit("http://localhost:5000/createNote");
cy.get('[data-cy="noteTitleInput"]').type("testtest1");
cy.get('[data-cy="createNoteButton"]').click();
cy.get('[data-cy="createNoteError"]').should(
"contain",
"Note title is too long."
);
});
if user credentials are incorrect, prompt error
it("if user credentials are incorrect, prompt error", () => {
cy.visit("http://localhost:5000/login");
cy.get('[data-cy="emailInput"]').type("user2404gmail.com");
cy.get('[data-cy="passwordInput"]').type("test404");
cy.get('[data-cy="userBtn"]').click();
cy.get("[data-cy='emailError']").should(
"contain",
"Please enter a valid email"
);
});
if user credentials are correct, redirect to note list page
it("if user credentials are correct, redirect to note list page", () => {
cy.visit("http://localhost:5000/login");
cy.get('[data-cy="emailInput"]').type("[email protected]");
cy.get('[data-cy="passwordInput"]').type("test123");
cy.get('[data-cy="userBtn"]').click();
cy.location().should((loc) => {
expect(loc.href).to.eq("http://localhost:5000/");
});
});
npm run api
GET /note
when jwt doesn't exists
test("when jwt doesn't exists", async () => {
const notes = await request(app).get("/note");
expect(notes.status).toEqual(401);
expect(notes.body.jwt_error).toEqual("Jwt doesn't exists");
});
when jwt is incorrect
test("when jwt is incorrect", async () => {
const notes = await request(app)
.get("/note")
.set("Authorization", "Bearer ssss");
expect(notes.status).toEqual(403);
expect(notes.body.error).toEqual("jwt malformed");
});
when jwt is expired
test("when jwt is expired", async () => {
const notes = await request(app)
.get("/note")
.set("Authorization", `Bearer ${jwt};`);
expect(notes.status).toEqual(403);
expect(notes.body.error).toEqual("invalid token");
});
POST /note
when jwt doesn't exists
test("when jwt doesn't exists", async () => {
const newNote = await request(app)
.post("/note")
.send({ title: "new note added in jest", content: "" });
expect(newNote.status).toBe(403);
expect(newNote.body.error).toBe("Jwt doesn't exist");
});
when jwt is incorrect
test("when jwt is incorrect", async () => {
const newNote = await request(app)
.post("/note")
.send({ title: "new note added in jest", content: "" })
.set("Authorization", "Bearer falseToken");
expect(newNote.status).toBe(403);
expect(newNote.body.error).toBe("jwt malformed");
});
when jwt is expired
test("when jwt is expired", async () => {
const newNote = await request(app)
.post("/note")
.send({ title: "just next note added in jest", content: "" })
.set("Authorization", `Bearer ${jwt}`);
expect(newNote.status).toBe(201);
expect(newNote.body.title).toBe("just next note added in jest");
});
POST /user
when credentials are correct
test("when credentials are correct", async () => {
let user = {
email: "[email protected]",
password: "testpassword123",
};
const newUser = await request(app).post("/user").send(user);
expect(newUser.body.email).toEqual(user.email);
expect(newUser.status).toEqual(200);
});
when credentials are incorrect
test("when credentials are incorrect", async () => {
let user = {
email: "testuser",
password: "test",
};
const newUser = await request(app).post("/user").send(user);
expect(newUser.body.errors).toBeTruthy();
expect(newUser.status).toEqual(400);
});
POST /user/authenticate
when credentials are correct
test("when credentials are correct", async () => {
let user = {
email: "[email protected]",
password: "testpassword123",
};
const newUser = await request(app).post("/user/authenticate").send(user);
expect(newUser.body.token).toBeTruthy();
expect(newUser.status).toEqual(201);
});
when credentials don't match user in db
test("when credentials don't match user in db", async () => {
let user = {
email: "testuser",
password: "test",
};
const newUser = await request(app).post("/user/authenticate").send(user);
expect(newUser.body.error).toBeTruthy();
expect(newUser.status).toEqual(400);
});
when credentials are incorrect
test("when credentials are incorrect", async () => {
let user = {
email: "[email protected]",
password: "test1234",
};
const newUser = await request(app).post("/user/authenticate").send(user);
expect(newUser.body.error).toBeTruthy();
expect(newUser.status).toEqual(400);
});