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

Restrict approve and reject buttons on the Task Request detail page to super_user only #896

Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
67 changes: 61 additions & 6 deletions __tests__/task-requests/task-requestDetails.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,61 @@ const {
urlMappings,
defaultMockResponseHeaders,
} = require('../../mock-data/taskRequests');
const { user } = require('../../mock-data/users/index.js');

describe('Request container for non-super users', () => {
let browser;
let page;
jest.setTimeout(60000);

beforeAll(async () => {
browser = await puppeteer.launch({
headless: 'new',
ignoreHTTPSErrors: true,
args: ['--incognito', '--disable-web-security'],
devtools: false,
});
page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request', (interceptedRequest) => {
const url = interceptedRequest.url();
if (url == 'https://staging-api.realdevsquad.com/users/self') {
interceptedRequest.respond({
...defaultMockResponseHeaders,
body: JSON.stringify(user),
});
} else if (urlMappings.hasOwnProperty(url)) {
interceptedRequest.respond({
...defaultMockResponseHeaders,
body: JSON.stringify(urlMappings[url]),
});
} else {
interceptedRequest.continue();
}
});
await page.goto(
'http://localhost:8000/task-requests/details/?id=dM5wwD9QsiTzi7eG7Oq5',
);
});

afterAll(async () => {
await browser.close();
});

it('Approve and Reject buttons should not render for non-super users', async function () {
const approveButton = await page.$('[data-testid="task-approve-button"]');
const rejectButton = await page.$('[data-testid="task-reject-button"]');
expect(approveButton).toBeNull();
expect(rejectButton).toBeNull();
});

it('Should render task status for non-super users', async function () {
const taskRequestStatus = await page.$(
'[data-testid="requestors-task-status"]',
);
expect(taskRequestStatus).toBeTruthy();
});
});

describe('Task request details page', () => {
let browser;
Expand Down Expand Up @@ -89,9 +144,9 @@ describe('Task request details page', () => {
);
});

it('Should contain Approve and Reject buttons', async function () {
const approveButton = await page.$('.requestors__conatainer__list__button');
const rejectButton = await page.$('.request-details__reject__button');
it('Should render Approve and Reject buttons for super users', async function () {
const approveButton = await page.$('[data-testid="task-approve-button"]');
const rejectButton = await page.$('[data-testid="task-reject-button"]');
expect(approveButton).toBeTruthy();
expect(rejectButton).toBeTruthy();
});
Expand Down Expand Up @@ -180,9 +235,9 @@ describe('Task request details page with markdown support in description', () =>
expect(descriptionHtmlValue).toContain('<h3 id="heading">Heading</h3>');
});

it('Should contain Approve and Reject buttons', async function () {
const approveButton = await page.$('.requestors__conatainer__list__button');
const rejectButton = await page.$('.request-details__reject__button');
it('Should render Approve and Reject buttons for super users', async function () {
const approveButton = await page.$('[data-testid="task-approve-button"]');
const rejectButton = await page.$('[data-testid="task-reject-button"]');
expect(approveButton).toBeTruthy();
expect(rejectButton).toBeTruthy();
});
Expand Down
5 changes: 3 additions & 2 deletions mock-data/taskRequests/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { superUserDetails } = require('../users/mockdata.js');

const fetchedTaskRequests = [
{
id: '123CCXSDF123',
Expand Down Expand Up @@ -260,7 +262,6 @@ const githubIssue = {
performed_via_github_app: null,
state_reason: 'completed',
};

const individualTaskDetail = {
message: 'task returned successfully',
taskData: {
Expand Down Expand Up @@ -306,7 +307,6 @@ const userInformationTaskCreation = {
},
},
};

const userInformation = {
message: 'User returned successfully!',
user: {
Expand Down Expand Up @@ -351,6 +351,7 @@ const defaultMockResponseHeaders = {
};

const urlMappings = {
'https://staging-api.realdevsquad.com/users/self': superUserDetails.user,
'https://api.realdevsquad.com/taskRequests/dM5wwD9QsiTzi7eG7Oq5':
individualTaskReqDetail,
'https://api.realdevsquad.com/taskRequests/dM5wwD9QsiTzi7eG7Oq6':
Expand Down
7 changes: 1 addition & 6 deletions task-requests/details/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script src="/task-requests/details/script.js" defer></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js"
integrity="sha512-LhccdVNGe2QMEfI3x4DVV3ckMRe36TfydKss6mJpdHjNFiV07dFpS2xzeZedptKZrwxfICJpez09iNioiSZ3hA=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script type="module" src="/task-requests/details/script.js" defer></script>
</head>
<body>
<div class="header">
Expand Down Expand Up @@ -85,11 +85,6 @@ <h4 class="requestors__container__title">Requestors</h4>
</ul>
</div>
</div>
<div class="reject__container">
<button id="reject-button" class="request-details__reject__button">
Reject
</button>
</div>
</div>
</div>
<div id="toast_task_details" class="hidden">
Expand Down
112 changes: 72 additions & 40 deletions task-requests/details/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ const API_BASE_URL =
window.location.hostname === 'localhost'
? 'https://staging-api.realdevsquad.com'
: window.API_BASE_URL;

import { getIsSuperUser } from '../../applications/utils.js';
let taskRequest;
let isSuperUser;

const taskRequestSkeleton = document.querySelector('.taskRequest__skeleton');
const container = document.querySelector('.container');
Expand All @@ -13,15 +14,15 @@ const requestorSkeleton = document.querySelector(
);

const taskRequestContainer = document.getElementById('task-request-details');
const requestDetailContainer =
document.getElementsByClassName('request-details');
const taskContainer = document.getElementById('task-details');
const toast = document.getElementById('toast_task_details');
const rejectButton = document.getElementById('reject-button');
const requestorsContainer = document.getElementById('requestors-details');
const taskRequestId = new URLSearchParams(window.location.search).get('id');
history.pushState({}, '', window.location.href);
const errorMessage =
'The requested operation could not be completed. Please try again later.';
let taskId;

function renderTaskRequestDetails(taskRequest) {
taskRequestContainer.append(
Expand Down Expand Up @@ -200,29 +201,38 @@ async function updateTaskRequest(action, userId) {
}
}

function getActionButton(requestor) {
if (taskRequest?.status === taskRequestStatus.APPROVED) {
if (taskRequest.approvedTo === requestor?.user?.id) {
return createCustomElement({
tagName: 'p',
textContent: 'Approved',
class: ['requestors__container__list__approved'],
});
} else {
return '';
function renderActionButton(requestor, taskRequest) {
if (isSuperUser) {
if (taskRequest?.status === taskRequestStatus.APPROVED) {
return taskRequest.approvedTo === requestor?.user?.id
? createCustomElement({
tagName: 'p',
textContent: 'Approved',
class: ['requestors__container__list__status'],
})
: '';
}
return createCustomElement({
tagName: 'button',
textContent: 'Approve',
class: 'requestors__conatainer__list__button',
'data-testid': 'task-approve-button',
eventListeners: [
{
event: 'click',
func: () =>
updateTaskRequest(TaskRequestAction.APPROVE, requestor.user?.id),
},
],
});
}
return createCustomElement({
tagName: 'button',
textContent: 'Approve',
class: 'requestors__conatainer__list__button',
eventListeners: [
{
event: 'click',
func: () =>
updateTaskRequest(TaskRequestAction.APPROVE, requestor.user?.id),
},
],
tagName: 'p',
textContent:
taskRequest.status[0].toUpperCase() +
taskRequest.status.slice(1).toLowerCase(),
class: ['requestors__container__list__status'],
'data-testid': 'requestors-task-status',
});
}

Expand Down Expand Up @@ -279,14 +289,18 @@ async function renderRequestors(taskRequest) {
createCustomElement({
tagName: 'div',
child: [
taskRequest.status !== 'DENIED' ? getActionButton(requestor) : '',
taskRequest.status !== 'DENIED'
? renderActionButton(requestor, taskRequest)
: createCustomElement({
tagName: 'p',
textContent: 'Denied',
class: ['requestors__container__list__status'],
'data-testid': 'requestor-container-task-status',
}),
],
}),
],
});
const avatarDiv = userDetailsDiv.querySelector(
'.requestors__container__list__userDetails__avatar',
);
requestorsContainer.append(userDetailsDiv);
});
}
Expand All @@ -305,7 +319,7 @@ async function fetchTaskRequest() {
}

const renderGithubIssue = async () => {
converter = new showdown.Converter({
const converter = new showdown.Converter({
tables: true,
simplifiedAutoLink: true,
tasklists: true,
Expand Down Expand Up @@ -344,7 +358,7 @@ const renderGithubIssue = async () => {
}),
);
const body = DOMPurify.sanitize(res?.body ?? '');
html = converter.makeHtml(body);
const html = converter.makeHtml(body);
taskContainer.appendChild(
createCustomElement({
tagName: 'div',
Expand Down Expand Up @@ -410,22 +424,40 @@ const renderGithubIssue = async () => {
);
};
const renderRejectButton = (taskRequest) => {
if (taskRequest?.status !== 'PENDING') {
rejectButton.disabled = true;
}
if (!isSuperUser) return;
if (taskRequest?.status === 'PENDING') {
const rejectContainer = createCustomElement({
tagName: 'div',
class: 'reject__container',
child: [
createCustomElement({
tagName: 'button',
textContent: 'Reject',
id: 'reject-button',
class: 'request-details__reject__button',
'data-testid': 'task-reject-button',
}),
],
});

rejectButton.addEventListener('click', async () => {
const res = await updateTaskRequest(TaskRequestAction.REJECT);
if (res?.ok) {
rejectButton.disabled = true;
}
});
requestDetailContainer[0].appendChild(rejectContainer);
const rejectButton = rejectContainer.querySelector('#reject-button');

rejectButton.addEventListener('click', async () => {
const res = await updateTaskRequest(TaskRequestAction.REJECT);
if (res?.ok) {
rejectButton.remove();
}
});
}
};

const renderTaskRequest = async () => {
taskRequestSkeleton.classList.remove('hidden');
taskContainer.classList.remove('hidden');
try {
taskRequest = await fetchTaskRequest();
isSuperUser = await getIsSuperUser();
taskRequestSkeleton.classList.add('hidden');
renderRejectButton(taskRequest);
renderTaskRequestDetails(taskRequest);
Expand Down Expand Up @@ -568,7 +600,7 @@ function populateModalContent(index) {
);

if (userData?.markdownEnabled ?? false) {
converter = new showdown.Converter({
const converter = new showdown.Converter({
tables: true,
simplifiedAutoLink: true,
tasklists: true,
Expand All @@ -577,7 +609,7 @@ function populateModalContent(index) {
openLinksInNewWindow: true,
});
const sanitizedDescription = DOMPurify.sanitize(userData.description ?? '');
html = converter.makeHtml(sanitizedDescription);
const html = converter.makeHtml(sanitizedDescription);
descriptionValue.innerHTML = html;
descriptionValue.className = 'requestor_description_details';
} else {
Expand Down
2 changes: 1 addition & 1 deletion task-requests/details/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ body {
background: #19805e;
transition: 0.3s ease-in-out;
}
.requestors__container__list__approved {
.requestors__container__list__status {
background: transparent;
border: none;
color: #c3c3c3;
Expand Down
2 changes: 2 additions & 0 deletions task-requests/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ function createCustomElement(domObjectMap) {
}
} else if (key === 'child') {
el.append(...value);
} else if (key.startsWith('data-')) {
el.setAttribute(key, value);
} else {
el[key] = value;
}
Expand Down
Loading