Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update userguide on how to run tests to check existence of endorse #87

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ec7acef
add deadline
github-classroom[bot] Sep 9, 2024
936b81c
Updated README.md with group member names
annashiheart Sep 13, 2024
6a6ec6d
added changes from project 1 to reduce cognitive complexity in src/ap…
evelynnchen-cmu Sep 21, 2024
f353adf
added Vicky's function from p1 into render.js in src/middleware
vickyc2266 Sep 21, 2024
59d1a6f
called the additional function in render.js
vickyc2266 Sep 21, 2024
d05dfb3
added test for the checkHeadersSent method in the middleware.js withi…
vickyc2266 Sep 21, 2024
f6587f8
fixed styling issues for render.js
vickyc2266 Sep 21, 2024
017c5dc
Fixed styling issues
vickyc2266 Sep 21, 2024
a30f699
Merge pull request #13 from CMU-313/evelynnc/merge-p1-changes
evelynnchen-cmu Sep 21, 2024
e59f8a7
added p1 changes to public/src/admin/appearance/themes.js
minghanm Sep 21, 2024
b877e68
Merge pull request #14 from CMU-313/vickyc/merge-p1-changes
vickyc2266 Sep 21, 2024
ddef8f5
set up config and changes from p1
annashiheart Sep 21, 2024
5c12daf
fix syntax errors in test/file.js
annashiheart Sep 21, 2024
8d13000
Merge pull request #15 from CMU-313/minghanm-merge-p1-changes
minghanm Sep 21, 2024
e255604
fixed syntax errors in file.js
annashiheart Sep 22, 2024
3c1a54c
removed dump.rdb file from pull request
annashiheart Sep 22, 2024
c8357d6
Merge pull request #16 from CMU-313/annashi/merge-p1-changes
annashiheart Sep 22, 2024
8011df1
Modify notification.js to reduce code complexity from 16 to 15
AliceeLe Sep 24, 2024
31ee45d
Merge pull request #17 from CMU-313/alice-p1-new
AliceeLe Sep 24, 2024
1c400da
Add new line at end of file to pass Github Actions test
AliceeLe Sep 24, 2024
5891d92
Merge pull request #18 from CMU-313/alice-p1-new
AliceeLe Sep 24, 2024
62046b4
added bool endorsed as a field to posts
evelynnchen-cmu Oct 7, 2024
aad6d86
fixed trailing comma and self assignment error
evelynnchen-cmu Oct 7, 2024
a723715
created simple API to toggle endorsement boolean along with test cases
evelynnchen-cmu Oct 7, 2024
6c8f586
Merge pull request #31 from CMU-313/evelynnc/endorsement-backend
evelynnchen-cmu Oct 7, 2024
f2369d6
fixed linting errors
evelynnchen-cmu Oct 7, 2024
bf409dd
Set up testing file for create.js
AliceeLe Oct 7, 2024
0133736
Unit test that upon initialization, endorse is false
AliceeLe Oct 7, 2024
cc1525a
Finalize test suite for create that is relevant with endorse
AliceeLe Oct 7, 2024
33a5bea
Merge pull request #32 from CMU-313/evelynnc/endorsement-backend
evelynnchen-cmu Oct 8, 2024
45092c4
Improve styling based on eslint suggestions
AliceeLe Oct 11, 2024
e72e91f
added UserGuide.md and filled in endorse story overview and automated…
evelynnchen-cmu Oct 11, 2024
d4f3377
Adjust styling
AliceeLe Oct 11, 2024
7581048
Merge branch 'f24' of https://github.com/CMU-313/nodebb-f24-boba into…
AliceeLe Oct 11, 2024
328c3bb
Update user guide on how to test for creation post
AliceeLe Oct 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"useTabs": true,
"arrowParens": "always",
"endOfLine": "auto",
"singleQuote": true,
"tabWidth": 1,
"requirePragma": false,
"insertPragma": false
}
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Names: Alice Le, Anna Shi, Cass Ma, Evelynn Chen, Vicky Chen

[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/ithVU1OO)
# ![NodeBB](public/images/sm-card.png)

[![Workflow](https://github.com/CMU-313/NodeBB/actions/workflows/test.yaml/badge.svg)](https://github.com/CMU-313/NodeBB/actions/workflows/test.yaml)
Expand Down
44 changes: 44 additions & 0 deletions UserGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
### 1. Feature Overview:

Feature Name: Endorse Post
Purpose: This feature enables TAs and professors to endorse posts, notifying all viewers that the content has been approved or supported by a professor or TA. It offers students a clear and reliable indicator of posts containing verified, legitimate information that they should prioritize reading.
Coder: Evelynn Chen, Vicky Chen, and Alice Le

### 2. Steps to test feature
Step 1: Sign into an authorized account (Professor or TA account)
Step 2: Enter and click into a published post
Step 3: Click on the Endorse button


### 3. Expected Result:
The endorse button on the post will no longer be available until someone unendorses the post by clicking on unendorse
(TA or Professor only)
Some sort of visual tag will appear on the post showing a TA or Professor has endorsed this post

### Automated Tests

### 1. Test Location
#### a.
**Location**:
- test/posts.js, lines [135-165](https://github.com/CMU-313/nodebb-f24-boba/blob/f24/test/posts.js#L135-L165)

### 2. What is Being Tested
- **Tested Features**: The API for toggling the endorsed field for a Post.
- **Test Type**: Unit tests / back-end tests.

### 3. Coverage Justification
- **Coverage Justification**:
The tests cover all use cases of toggling the boolean field endorsed. By default, a post is not endorsed, and only one request to endorse/unendorse at a time should be processed.

#### b.
**Location**:
Location: test/posts/create.js in Alice/Feature-endorse branch

### 2. What is Being Tested
**Tested Features**: The creation of posts with the endorsed attribute, verifying default behavior (where endorsed is set to false) and the initialization of endorsed to true as specified by the user.
**Test Type**: Unit tests and back-end validation tests ensure that the endorsed field is handled correctly during post creation.

### 3. Why the Tests Are Sufficient
**Coverage Justification**: These tests cover critical scenarios around the endorsed attribute in post creation, ensuring both default and custom initialization works as intended. The default behavior test confirms that when endorsed is unspecified, the field defaults to false, maintaining standard post behavior. The specified behavior test validates that setting endorsed to true explicitly reflects in the backend data, demonstrating reliable customization.
**Edge Cases**: The tests handle potential edge cases, such as a null endorsed value or unexpected input, maintaining data integrity and preventing unauthorized attribute modification.
**Exclusion of Front-End Tests**: The tests are back-end focused, as UI interactions for post endorsement are manually validated. The primary emphasis remains on data accuracy in the database, which is critical for user-facing endorsement functionality.
Binary file added dump.rdb
Binary file not shown.
2 changes: 2 additions & 0 deletions public/openapi/components/schemas/PostObject.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ PostObject:
type: number
votes:
type: number
endorsed:
type: boolean
timestampISO:
type: string
description: An ISO 8601 formatted date string (complementing `timestamp`)
Expand Down
2 changes: 2 additions & 0 deletions public/openapi/read/topic/topic_id.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ get:
type: number
downvotes:
type: number
endorsed:
type: number
bookmarks:
type: number
deleterUid:
Expand Down
2 changes: 2 additions & 0 deletions public/openapi/write/posts/pid.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ get:
type: number
votes:
type: number
endorsed:
type: number
timestampISO:
type: string
description: An ISO 8601 formatted date string (complementing `timestamp`)
Expand Down
2 changes: 2 additions & 0 deletions public/openapi/write/posts/pid/replies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ get:
type: number
bookmarks:
type: number
endorsed:
type: number
deleterUid:
type: number
edited:
Expand Down
145 changes: 79 additions & 66 deletions public/src/admin/appearance/themes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,87 +10,100 @@ define('admin/appearance/themes', ['bootbox', 'translator', 'alerts'], function
const action = target.attr('data-action');

if (action && action === 'use') {
const parentEl = target.parents('[data-theme]');
const themeType = parentEl.attr('data-type');
const cssSrc = parentEl.attr('data-css');
const themeId = parentEl.attr('data-theme');

if (config['theme:id'] === themeId) {
return;
}
handleThemeChange(target);
}
});
};

function handleThemeChange(target) {
const parentEl = target.parents('[data-theme]');
const themeType = parentEl.attr('data-type');
const cssSrc = parentEl.attr('data-css');
const themeId = parentEl.attr('data-theme');

if (config['theme:id'] === themeId) {
return;
}
setTheme(themeType, themeId, cssSrc);
}

function setTheme(themeType, themeId, cssSrc) {
socket.emit('admin.themes.set', {
type: themeType,
id: themeId,
src: cssSrc,
}, function (err) {
if (err) {
return alerts.error(err);
}
config['theme:id'] = themeId;
highlightSelectedTheme(themeId);
showThemeChangeAlert();
});
}

function showThemeChangeAlert() {
alerts.alert({
alert_id: 'admin:theme',
type: 'info',
title: '[[admin/appearance/themes:theme-changed]]',
message: '[[admin/appearance/themes:restart-to-activate]]',
timeout: 5000,
clickfn: rebuildAndRestartInstance,
});
}

function rebuildAndRestartInstance() {
require(['admin/modules/instance'], function (instance) {
instance.rebuildAndRestart();
});
}

$('#revert_theme').on('click', function () {
if (config['theme:id'] === 'nodebb-theme-harmony') {
return;
}
bootbox.confirm('[[admin/appearance/themes:revert-confirm]]', function (confirm) {
if (confirm) {
socket.emit('admin.themes.set', {
type: themeType,
id: themeId,
src: cssSrc,
type: 'local',
id: 'nodebb-theme-harmony',
}, function (err) {
if (err) {
return alerts.error(err);
}
config['theme:id'] = themeId;
highlightSelectedTheme(themeId);

config['theme:id'] = 'nodebb-theme-harmony';
highlightSelectedTheme('nodebb-theme-harmony');
alerts.alert({
alert_id: 'admin:theme',
type: 'info',
type: 'success',
title: '[[admin/appearance/themes:theme-changed]]',
message: '[[admin/appearance/themes:restart-to-activate]]',
timeout: 5000,
clickfn: function () {
require(['admin/modules/instance'], function (instance) {
instance.rebuildAndRestart();
});
},
message: '[[admin/appearance/themes:revert-success]]',
timeout: 3500,
});
});
}
});
});

$('#revert_theme').on('click', function () {
if (config['theme:id'] === 'nodebb-theme-harmony') {
return;
}
bootbox.confirm('[[admin/appearance/themes:revert-confirm]]', function (confirm) {
if (confirm) {
socket.emit('admin.themes.set', {
type: 'local',
id: 'nodebb-theme-harmony',
}, function (err) {
if (err) {
return alerts.error(err);
}
config['theme:id'] = 'nodebb-theme-harmony';
highlightSelectedTheme('nodebb-theme-harmony');
alerts.alert({
alert_id: 'admin:theme',
type: 'success',
title: '[[admin/appearance/themes:theme-changed]]',
message: '[[admin/appearance/themes:revert-success]]',
timeout: 3500,
});
});
}
});
});

socket.emit('admin.themes.getInstalled', function (err, themes) {
if (err) {
return alerts.error(err);
}
socket.emit('admin.themes.getInstalled', function (err, themes) {
if (err) {
return alerts.error(err);
}

const instListEl = $('#installed_themes');
const instListEl = $('#installed_themes');

if (!themes.length) {
instListEl.append($('<li/ >').addClass('no-themes').translateHtml('[[admin/appearance/themes:no-themes]]'));
} else {
app.parseAndTranslate('admin/partials/theme_list', {
themes: themes,
}, function (html) {
instListEl.html(html);
highlightSelectedTheme(config['theme:id']);
});
}
});
};
if (!themes.length) {
instListEl.append($('<li/ >').addClass('no-themes').translateHtml('[[admin/appearance/themes:no-themes]]'));
} else {
app.parseAndTranslate('admin/partials/theme_list', {
themes: themes,
}, function (html) {
instListEl.html(html);
highlightSelectedTheme(config['theme:id']);
});
}
});

function highlightSelectedTheme(themeId) {
translator.translate('[[admin/appearance/themes:select-theme]] || [[admin/appearance/themes:current-theme]]', function (text) {
Expand Down
41 changes: 23 additions & 18 deletions public/src/modules/settings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
'use strict';

function updateValue(trim, value, element) {
console.log('Anna Shi');
print('Anna');
if (trim && value && typeof value.trim === 'function') {
value = value.trim();
if (typeof value.toString === 'function') {
value = value.toString();
}
} else if (value != null) {
if (typeof value.toString === 'function') {
value = value.toString();
}
if (trim) {
value = value.trim();
}
} else {
value = '';
}
if (value !== undefined) {
element.val(value);
}
}

define('settings', ['hooks', 'alerts'], function (hooks, alerts) {
// eslint-disable-next-line prefer-const
Expand Down Expand Up @@ -183,24 +205,7 @@ define('settings', ['hooks', 'alerts'], function (hooks, alerts) {
if (value instanceof Array) {
value = value.join(element.data('split') || (trim ? ', ' : ','));
}
if (trim && value && typeof value.trim === 'function') {
value = value.trim();
if (typeof value.toString === 'function') {
value = value.toString();
}
} else if (value != null) {
if (typeof value.toString === 'function') {
value = value.toString();
}
if (trim) {
value = value.trim();
}
} else {
value = '';
}
if (value !== undefined) {
element.val(value);
}
updateValue(trim, value, element);
},
/**
Calls the init-hook and {@link helper.fillField} on each field within wrapper-object.
Expand Down
27 changes: 20 additions & 7 deletions src/api/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,26 +118,39 @@ async function canSearchMembers(uid, groupName) {
}
}

groupsAPI.join = async function (caller, data) {
function validateJoinRequest(caller, data) {
if (!data) {
throw new Error('[[error:invalid-data]]');
}
if (caller.uid <= 0 || !data.uid) {
throw new Error('[[error:invalid-uid]]');
}
}

const groupName = await groups.getGroupNameByGroupSlug(data.slug);
if (!groupName) {
throw new Error('[[error:no-group]]');
}

async function checkPrivileges(caller, groupName) {
const isCallerAdmin = await privileges.admin.can('admin:groups', caller.uid);
if (!isCallerAdmin && (
groups.systemGroups.includes(groupName) ||
groups.isPrivilegeGroup(groupName)
)) {
throw new Error('[[error:not-allowed]]');
}
return isCallerAdmin;
}

function isGroupJoinDisabledForCaller(isCallerAdmin, isSelf, groupData) {
return !isCallerAdmin && isSelf && groupData.private && groupData.disableJoinRequests;
}

groupsAPI.join = async function (caller, data) {
validateJoinRequest(caller, data);

const groupName = await groups.getGroupNameByGroupSlug(data.slug);
if (!groupName) {
throw new Error('[[error:no-group]]');
}

const isCallerAdmin = await checkPrivileges(caller, groupName);

const [groupData, userExists] = await Promise.all([
groups.getGroupData(groupName),
Expand All @@ -159,7 +172,7 @@ groupsAPI.join = async function (caller, data) {
return;
}

if (!isCallerAdmin && isSelf && groupData.private && groupData.disableJoinRequests) {
if (isGroupJoinDisabledForCaller(isCallerAdmin, isSelf, groupData)) {
throw new Error('[[error:group-join-disabled]]');
}

Expand Down
4 changes: 4 additions & 0 deletions src/api/posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -512,3 +512,7 @@ postsAPI.getReplies = async (caller, { pid }) => {

return postData;
};

postsAPI.endorse = async (caller, { pid }) => await posts.endorse(pid, caller.uid);

postsAPI.unendorse = async (caller, { pid }) => await posts.unendorse(pid, caller.uid);
Loading
Loading