forked from CMU-17313Q/cmu-17313q-f24-nodebb-f24-NodeBB
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
388 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# User Guide for additional NodeBB Features | ||
|
||
## Introduction | ||
This document provides an outline of the new features implemented in the NodeBB platform. The following functionalities have been added to enhance user experience for both instructors and students: | ||
|
||
1. Marking answers as "Instructor-Approved." | ||
2. Sharing posts anonymously. | ||
3. Displaying estimated reading time for each post. | ||
|
||
## Feature 1: Marking Answers as "Instructor-Approved" | ||
|
||
### Description | ||
Instructors can now mark specific answers as "Instructor-Approved." This feature helps students easily identify which responses are accurate and verified by their instructors. | ||
|
||
### How to Use | ||
1. **For Instructors:** | ||
Log in using the following: | ||
- Username: Instructor | ||
- Password: password | ||
- Navigate to the post containing the answers. | ||
- Hover over the answer you want to approve. | ||
- toggle on the **"Mark as Approved"** button that appears. | ||
- The answer will now be highlighted, indicating that it has been approved by an instructor. | ||
|
||
2. **For Students:** | ||
Log in using the following: | ||
- Username: Student | ||
- Password: password | ||
- Look for answers marked with the "Instructor-Approved" label. | ||
- Engage with these answers confidently, knowing they are verified. | ||
|
||
### Testing | ||
Automated tests for this feature are located in the `test/newImplementedFeatures/instructorApproved.js` file. The tests cover: | ||
- Approval of an answer by an instructor. | ||
- Verification that approved answers are highlighted appropriately. | ||
|
||
--- | ||
|
||
## Feature 2: Sharing Posts Anonymously | ||
|
||
### Description | ||
Students can now share posts anonymously, allowing them to ask questions or share information without revealing their identities. This feature promotes a more open and peaceful discussion environment. | ||
|
||
### How to Use | ||
1. **For Students:** | ||
- When creating a new post, check the **"Post Anonymously"** option. you can choose to be anonymous to all, none or your students. | ||
- Submit your post as usual. Your identity will remain hidden from other users. | ||
|
||
### Testing | ||
Automated tests for this feature can be found in the `test/newImplementedFeatures/anonymousFeature.js` file. The tests validate: | ||
- Successful submission of an anonymous post. | ||
- Proper display of anonymous posts without revealing the user’s identity. | ||
|
||
--- | ||
|
||
## Feature 3: Estimated Reading Time for Posts | ||
|
||
### Description | ||
Each post now includes an estimated reading time, helping students decide whether they have sufficient time to read the content before engaging. | ||
|
||
### How to Use | ||
1. **For Students:** | ||
- When viewing a post, look for the estimated reading time displayed at the top right of the post. | ||
- Use this information to gauge if you can read the content within your available time. | ||
- This implementation can be seen when you click on discussion and then click on the post. Within the post, you can see the estimated reading time for each of the posts. | ||
|
||
### Testing | ||
Automated tests for this feature are located in the `test/newImplementedFeatures/EstimatedTimeForReadingPost.js` file. The tests ensure: | ||
- Accurate calculation of estimated reading time based on the post's word count. | ||
- Proper display of reading time in the post view. | ||
|
||
--- | ||
|
||
## Conclusion | ||
These new features aim to enhance the overall experience for instructors and students on the NodeBB platform. By providing verified answers, allowing anonymous discussions, and indicating reading times, we hope to foster a more effective and supportive learning environment. |
84 changes: 67 additions & 17 deletions
84
node_modules/nodebb-theme-harmony/templates/partials/topic/post.tpl
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,6 +70,8 @@ | |
{{{ end }}} | ||
<strong>{{{ if posts.data.tid }}}[[post-queue:reply]]{{{ else }}}[[post-queue:topic]]{{{ end }}}</strong> | ||
<span class="timeago float-end" title={posts.data.timestampISO}></span> | ||
<!-- Add the reading time --> | ||
<span id="reading-time-{posts.data.id}" class="text-muted"></span> | ||
</div> | ||
<div class="card-body"> | ||
<div class="row"> | ||
|
@@ -129,11 +131,11 @@ | |
<button class="btn btn-danger btn-sm" data-action="reject"><i class="fa fa-fw fa-times"></i> [[post-queue:remove]]</button> | ||
{{{ end }}} | ||
</div> | ||
</div> | ||
</div>[email protected] | ||
</div> | ||
{{{ end }}} | ||
</div> | ||
<!-- IMPORT partials/paginator.tpl --> | ||
</div> | ||
</div> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
'use strict'; | ||
|
||
const { JSDOM } = require('jsdom'); | ||
const { expect } = require('chai'); | ||
const request = require('supertest'); | ||
const app = require('../../app'); | ||
const user = require('../../src/user'); | ||
const topics = require('../../src/topics'); | ||
const posts = require('../../src/posts'); | ||
|
||
describe('Anonymous Feature - Share Post Anonymously', () => { | ||
let studentToken; | ||
let postId; | ||
let topicId; | ||
|
||
before(async () => { | ||
// Create a student user | ||
const student = await user.create({ username: 'student', password: 'password' }); | ||
|
||
// Log in as student to get token | ||
studentToken = await loginUser('student', 'password'); | ||
|
||
// Create a topic and a post | ||
const topic = await topics.post({ | ||
title: 'Test Topic', | ||
content: 'Test Content', | ||
uid: student.uid, | ||
cid: 1, | ||
}); | ||
topicId = topic.tid; | ||
const post = await posts.create({ | ||
content: 'Test Post', | ||
uid: student.uid, | ||
tid: topic.tid, | ||
annonymousType: 'student', | ||
}); | ||
postId = post.pid; | ||
}); | ||
|
||
async function loginUser(username, password) { | ||
const res = await request(app) | ||
.post('/api/v1/login') | ||
.send({ username, password }); | ||
return res.body.token; | ||
} | ||
|
||
describe('UI Elements', () => { | ||
it('should display the anonymous type dropdown', () => { | ||
const dom = new JSDOM(` | ||
<div class="form-group"> | ||
<label for="anonymousDropdown">[[topic:composer.select-anonymous-type]]</label> | ||
<select class="form-control" id="anonymousDropdown" name="anonymousType"> | ||
<option value="none">[[topic:composer-anonymous-none]]</option> | ||
<option value="student">[[topic:composer-anonymous-student]]</option> | ||
<option value="all">[[topic:composer-anonymous-all]]</option> | ||
</select> | ||
</div> | ||
`); | ||
const { document } = dom.window; | ||
const dropdown = document.getElementById('anonymousDropdown'); | ||
expect(dropdown).to.not.be.null; | ||
expect(dropdown.options.length).to.equal(3); | ||
}); | ||
}); | ||
|
||
describe('Form Submission', () => { | ||
it('should include the anonymous type in the form submission', async () => { | ||
const res = await request(app) | ||
.post('/api/v1/topics') | ||
.set('Authorization', `Bearer ${studentToken}`) | ||
.send({ | ||
title: 'Anonymous Post', | ||
content: 'This is an anonymous post.', | ||
cid: 1, | ||
annonymousType: 'student', | ||
}) | ||
.expect(200); | ||
|
||
expect(res.body.topic.annonymousType).to.equal('student'); | ||
}); | ||
}); | ||
|
||
describe('Backend Handling', () => { | ||
it('should display the post anonymously based on the selected type', async () => { | ||
const res = await request(app) | ||
.get(`/api/v1/posts/${postId}`) | ||
.expect(200); | ||
|
||
const { post } = res.body; | ||
if (post.annonymousType === 'none') { | ||
expect(post.user.username).to.not.be.null; | ||
} else { | ||
expect(post.user.username).to.be.null; | ||
} | ||
}); | ||
}); | ||
}); |
45 changes: 45 additions & 0 deletions
45
test/newImplementedFeatures/estimatedTimeForReadingPost.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
'use strict'; | ||
|
||
const { JSDOM } = require('jsdom'); | ||
const { expect } = require('chai'); | ||
|
||
describe('Estimated Time for Reading Post', () => { | ||
let document; | ||
|
||
beforeEach(() => { | ||
const dom = new JSDOM(` | ||
<div id="post-content-1">This is a test post content with a few words.</div> | ||
<span id="reading-time-1" class="text-muted me-3"></span> | ||
`); | ||
document = dom.window.document; | ||
}); | ||
|
||
it('should calculate the word count correctly', () => { | ||
const postContentElement = document.getElementById('post-content-1'); | ||
const postContent = postContentElement.textContent; | ||
const wordCount = postContent.trim().split(/\s+/).length; | ||
|
||
expect(wordCount).to.equal(9); // Adjust the expected word count based on the content | ||
}); | ||
|
||
it('should estimate the reading time correctly', () => { | ||
const wordCount = 9; // Use the word count from the previous test | ||
const wordsPerMinute = 200; | ||
const readingTime = Math.ceil(wordCount / wordsPerMinute); | ||
|
||
expect(readingTime).to.equal(1); // Adjust the expected reading time based on the word count | ||
}); | ||
|
||
it('should display the estimated reading time in the DOM', () => { | ||
const postContentElement = document.getElementById('post-content-1'); | ||
const readingTimeElement = document.getElementById('reading-time-1'); | ||
|
||
const postContent = postContentElement.textContent; | ||
const wordCount = postContent.trim().split(/\s+/).length; | ||
const readingTime = Math.ceil(wordCount / 200); | ||
|
||
readingTimeElement.textContent = `Estimated reading time: ${readingTime} min`; | ||
|
||
expect(readingTimeElement.textContent).to.equal('Estimated reading time: 1 min'); // Adjust the expected text based on the reading time | ||
}); | ||
}); |
Oops, something went wrong.