+
+ {pay.payment_type || 'Payment'}
+
{moment(pay.created).format('MM/DD/YY')} |
-
+ |
{formatSat(pay.amount)} sats
|
-
+ |
{
/>
|
{pay.payment_type === 'payment' ? : null} |
-
+ |
{pay.payment_type === 'payment' ? (
{
{pay.payment_type === 'payment' ? (
- viewBounty(pay.bounty_id)}>
+ viewBounty(pay.bounty_id)}
+ data-testid={`payment-history-transaction-link`}
+ >
View bounty
diff --git a/src/people/widgetViews/postBounty/__tests__/PostModal.spec.tsx b/src/people/widgetViews/postBounty/__tests__/PostModal.spec.tsx
index 0f212bbda..11a07d057 100644
--- a/src/people/widgetViews/postBounty/__tests__/PostModal.spec.tsx
+++ b/src/people/widgetViews/postBounty/__tests__/PostModal.spec.tsx
@@ -1,5 +1,5 @@
import '@testing-library/jest-dom';
-import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import nock from 'nock';
import React from 'react';
@@ -15,71 +15,110 @@ beforeAll(() => {
});
describe('Post bounty modal', () => {
- nock(user.url).get('/person/id/1').reply(200, {});
+ nock(user.url).get('/person/id/1').reply(200, { user });
+ nock(user.url).get('/ask').reply(200, {});
- test('placeholder', () => {});
-
- /*test('Show and close modal', () => {
+ test('clicking on post a bounty button render a form', () => {
const closeHandler = jest.fn();
- render();
+ render();
expect(screen.getByRole('alertdialog')).toBeInTheDocument();
- fireEvent.click(screen.getByTestId('close-btn'));
- expect(closeHandler).toBeCalledTimes(1);
+ expect(screen.getByText(/Choose Bounty type/i)).toBeInTheDocument();
});
- test('If modal closed it isnt in the DOM', () => {
+
+ test('start button is visible and navigates to the first step', () => {
const closeHandler = jest.fn();
- render();
- expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument();
+ render();
+ const startButton = screen.getByText('Start');
+ expect(startButton).toBeInTheDocument();
+ fireEvent.click(startButton);
+ expect(screen.getByText('Basic info')).toBeInTheDocument();
});
- const formData = {
- organization: 'organization',
- title: 'title',
- category: 'Web development',
- description: 'description',
- price: 1
- };
- test('FillForm', async () => {
+ test('back and the next button take you forward and backward respectively', () => {
const closeHandler = jest.fn();
- const successHandler = jest.fn(() => {});
- render(
-
- );
+ render();
+ const startButton = screen.getByText('Start');
+ fireEvent.click(startButton);
+ fireEvent.click(screen.getByText('Next'));
+ expect(screen.getByText('Freelance Job Request')).toBeInTheDocument();
+ fireEvent.click(screen.getByText('Back'));
+ expect(screen.getByText('Choose Bounty type')).toBeInTheDocument();
+ });
- // 1step
- expect(screen.queryByText('Choose Bounty type')).toBeInTheDocument();
- const button = await screen.findByText('Start');
- fireEvent.click(button);
+ test('all form field are rendered', () => {
+ const closeHandler = jest.fn();
+ render();
+ const startButton = screen.getByText('Start');
+ fireEvent.click(startButton);
+ expect(screen.getByText('Basic info')).toBeInTheDocument();
+ const Form1 = screen.getByText('Organization (optional)');
+ const Form2 = screen.getByText('Bounty Title *');
+ const Form3 = screen.getByText('Github Issue URL');
+ const Form4 = screen.getByText('Category *');
+ const Form5 = screen.getByText('Coding Language');
+ expect(Form1).toBeInTheDocument();
+ expect(Form2).toBeInTheDocument();
+ expect(Form3).toBeInTheDocument();
+ expect(Form4).toBeInTheDocument();
+ expect(Form5).toBeInTheDocument();
+ });
+
+ test('clicking on assign hunter', () => {
+ const closeHandler = jest.fn();
+ const formData = {
+ organization: 'organization',
+ title: 'title',
+ category: 'Web development',
+ description: 'description',
+ price: 1
+ };
+ render();
+ fireEvent.click(screen.getByText('Start'));
- // 2 step
expect(screen.queryByText('Basic info')).toBeInTheDocument();
expect(screen.queryByText('Next')).toHaveClass('disableText');
- await waitFor(async () => {
+ waitFor(async () => {
await userEvent.type(screen.getByLabelText('Bounty Title'), formData.title);
- await userEvent.click(screen.getByTestId('Category'));
- await userEvent.click(screen.getByText(formData.category));
- await userEvent.click(screen.getByText('Next'));
- });
+ userEvent.click(screen.getByTestId('Category'));
+ userEvent.click(screen.getByText(formData.category));
+ userEvent.click(screen.getByText('Next'));
+ expect(screen.getByText('Description')).toBeInTheDocument();
+ expect(screen.queryAllByText('Description')[0]).toBeInTheDocument();
+ await waitFor(async () => {
+ await userEvent.type(screen.getByLabelText('Description'), formData.description);
+ userEvent.click(screen.getByText('Next'));
+ });
+ expect(screen.getByText('Price and Estimate')).toBeInTheDocument();
+ await waitFor(async () => {
+ await userEvent.type(screen.getByLabelText('Price (Sats)*'), String(formData.price));
+ userEvent.click(screen.getByText('Next'));
+ });
+ expect(screen.queryByText('Assign Developer')).toBeInTheDocument();
+ await waitFor(async () => {
+ userEvent.click(await screen.findByText('Decide Later'));
+ });
+ expect(screen.queryByText('Finish')).toBeInTheDocument();
- // 3 step
- expect(screen.queryAllByText('Description')[0]).toBeInTheDocument();
- await waitFor(async () => {
- await userEvent.type(screen.getByLabelText('Description'), formData.description);
- await userEvent.click(screen.getByText('Next'));
- });
+ await waitFor(() => {
+ expect(screen.getByText('Type to search')).toBeInTheDocument();
+ expect(screen.getByText('Skills')).toBeInTheDocument();
+ const assignButtons = screen.getAllByText('Assign');
+ expect(assignButtons.length).toBe(5);
+ });
- //4 step
- expect(screen.queryByText('Price and Estimate')).toBeInTheDocument();
- await waitFor(async () => {
- await userEvent.type(screen.getByLabelText('Price (Sats)*'), String(formData.price));
- await userEvent.click(screen.getByText('Next'));
- });
+ // Test That on clicking on the "type to search" box I should be able to type in any character and the list below should be filtered.
+ const searchBox = screen.getByPlaceholderText('Type to search ...');
+ await userEvent.type(searchBox, 'John Doe');
- //5 step
- expect(screen.queryByText('Assign Developer')).toBeInTheDocument();
- await waitFor(async () => {
- await userEvent.click(await screen.findByText('Decide Later'));
+ // Test that on clicking on the "skills" box I should be able to type in any character and the list below should be filtered.
+ userEvent.click(screen.getByRole('button', { name: /Skills/i }));
+ const dropdown = screen.getByRole('listbox');
+ userEvent.click(within(dropdown).getByText('JavaScript'));
+ userEvent.click(within(dropdown).getByText('Python'));
+ userEvent.click(within(dropdown).getByText('Golang'));
+ expect(screen.getByText('JavaScript')).toBeVisible();
+ expect(screen.getByText('Python')).toBeVisible();
+ expect(screen.getByText('Golang')).toBeVisible();
});
- expect(screen.queryByText('Finish')).toBeInTheDocument();
- });*/
+ });
});
diff --git a/src/people/widgetViews/statusFilterCount.spec.tsx b/src/people/widgetViews/statusFilterCount.spec.tsx
index 1cd189dad..d76d131cd 100644
--- a/src/people/widgetViews/statusFilterCount.spec.tsx
+++ b/src/people/widgetViews/statusFilterCount.spec.tsx
@@ -28,7 +28,7 @@ describe('BountyHeader', () => {
it('displays the filter button and shows status count on click', async () => {
render(
{
render(
$@
{
@@ -893,11 +895,14 @@ function MobileView(props: CodingBountiesProps) {
/>
SAT
- {satToUsd(bountyPrice)} USD
+
+ {satToUsd(bountyPrice)} USD
+
({}));
+
+jest.mock('rehype-raw', () => ({}));
+
describe('MobileView component', () => {
beforeEach(() => {
const mockIntersectionObserver = jest.fn();
@@ -16,6 +31,10 @@ describe('MobileView component', () => {
window.IntersectionObserver = mockIntersectionObserver;
});
+ afterAll(() => {
+ jest.clearAllMocks();
+ });
+
const defaultProps: CodingBountiesProps = {
deliverables: 'Default Deliverables',
description: 'Default Description',
@@ -143,7 +162,7 @@ describe('MobileView component', () => {
id: 180,
owner: 'Test-Owner',
owner_pubkey: 'abc100',
- widget: 'wanted'
+ widget: 'bounties'
};
render(} />);
@@ -163,4 +182,268 @@ describe('MobileView component', () => {
const completionDate = screen.getByText('Jan 26, 2024');
expect(completionDate).toBeInTheDocument();
});
+
+ it('Test that on clicking on "not assigned", a pop up should appear to invite a developer including "type to search" box, a "skills" box, and a recommendation of 5 hunters.', async () => {
+ const props: CodingBountiesProps = {
+ ...defaultProps,
+ assigneeValue: true,
+ creatorStep: 0,
+ peopleList: [
+ { id: 1, owner_alias: '111' },
+ { id: 2, owner_alias: '222' },
+ { id: 3, owner_alias: '333' },
+ { id: 4, owner_alias: '444' },
+ { id: 5, owner_alias: '555' }
+ ] as any
+ };
+
+ uiStore.setMeInfo({
+ ...user,
+ owner_alias: props.person.owner_alias
+ });
+
+ render();
+
+ fireEvent.click(screen.getByText('Not Assigned'));
+
+ await waitFor(() => {
+ expect(screen.getByText('Assign Developer')).toBeInTheDocument();
+ expect(screen.getByPlaceholderText('Type to search ...')).toBeInTheDocument();
+ expect(screen.getByText('Skills')).toBeInTheDocument();
+ expect(document.querySelectorAll('.PeopleList .People')).toHaveLength(5);
+ });
+ });
+
+ it('Test filter peopleList by "type to search" or "skills"', async () => {
+ const props: CodingBountiesProps = {
+ ...defaultProps,
+ assigneeValue: true,
+ creatorStep: 0,
+ peopleList: [
+ { id: 1, owner_alias: '111' },
+ { id: 2, owner_alias: '222' },
+ { id: 3, owner_alias: '333' },
+ { id: 4, owner_alias: '444' },
+ { id: 5, owner_alias: '555' }
+ ] as any
+ };
+
+ uiStore.setMeInfo({
+ ...user,
+ owner_alias: props.person.owner_alias
+ });
+
+ const mockPeopleList: any = [
+ {
+ id: 1,
+ owner_alias: 'TEST_NAME_1',
+ extras: {
+ coding_languages: [
+ { value: 'R', label: 'R' },
+ { value: 'C++', label: 'C++' }
+ ]
+ }
+ },
+ {
+ id: 2,
+ owner_alias: 'TEST_NAME_2',
+ extras: { coding_languages: [{ value: 'C', label: 'C' }] }
+ },
+ { id: 3, owner_alias: 'TEST_NAME_3', extras: { coding_languages: [] } }
+ ];
+ const mockSearch = jest
+ .spyOn(mainStore, 'getPeopleByNameAliasPubkey')
+ .mockResolvedValue(mockPeopleList);
+
+ render();
+
+ fireEvent.click(screen.getByText('Not Assigned'));
+
+ // filter by "type to search"
+ await waitFor(async () => {
+ expect(screen.getByText('Assign Developer')).toBeInTheDocument();
+ fireEvent.click(screen.getByPlaceholderText('Type to search ...'));
+ await userEvent.type(screen.getByPlaceholderText('Type to search ...'), 'TEST_NAME');
+ expect(document.querySelectorAll('.PeopleList .People')).toHaveLength(mockPeopleList.length);
+
+ mockPeopleList.forEach((person: any) => {
+ expect(screen.getByText(person.owner_alias)).toBeInTheDocument();
+ });
+ });
+
+ // filter by "skills"
+ await waitFor(() => {
+ fireEvent.click(screen.getByText('Skills'));
+ fireEvent.click(screen.getByText('R'));
+
+ fireEvent.keyDown(document, { key: 'Escape', keyCode: 27 });
+ });
+
+ expect(document.querySelectorAll('.PeopleList .People')).toHaveLength(1);
+ expect(screen.getByText('TEST_NAME_1')).toBeInTheDocument();
+ expect(screen.queryByText('TEST_NAME_2')).not.toBeInTheDocument();
+ expect(screen.queryByText('TEST_NAME_3')).not.toBeInTheDocument();
+
+ mockSearch.mockRestore();
+ });
+
+ it('Test that on clicking on "Assign" on a hunter, the pop up should clear and the hunter should be assigned to the bounty', async () => {
+ const props: CodingBountiesProps = {
+ ...defaultProps,
+ creatorStep: 0,
+ peopleList: [
+ { id: 1, owner_alias: 'NAME_1' },
+ { id: 2, owner_alias: 'NAME_2' },
+ { id: 3, owner_alias: 'NAME_3' },
+ { id: 4, owner_alias: 'NAME_4' },
+ { id: 5, owner_alias: 'NAME_5' }
+ ] as any
+ };
+ const mockHandleAssigneeDetails = jest.fn();
+
+ uiStore.setMeInfo({
+ ...user,
+ owner_alias: props.person.owner_alias
+ });
+
+ const App = () => {
+ const [assigneeValue, setAssigneeValue] = useState(false);
+
+ return (
+ setAssigneeValue((v: boolean) => !v)}
+ handleAssigneeDetails={() => {
+ setAssigneeValue((v: boolean) => !v);
+ mockHandleAssigneeDetails();
+ }}
+ />
+ );
+ };
+
+ render();
+
+ fireEvent.click(screen.getByText('Not Assigned'));
+
+ await waitFor(() => {
+ expect(screen.getByText('Assign Developer')).toBeInTheDocument();
+ fireEvent.click(screen.getAllByText('Assign')[0]);
+ });
+
+ await waitFor(() => {
+ expect(screen.queryByText('Assign Developer')).toBe(null);
+ expect(mockHandleAssigneeDetails).toBeCalledTimes(1);
+ });
+ });
+
+ it('Test that mark as paid button, renders: adjust amount , assignee name, amount set in Sats and next button', async () => {
+ const props: CodingBountiesProps = {
+ ...defaultProps,
+ paid: false
+ };
+
+ uiStore.setMeInfo({
+ ...user,
+ owner_alias: props.person.owner_alias
+ });
+
+ render();
+
+ const inputSAT = screen.findByTestId('input_sats');
+ const nextBTN = screen.findByTestId('next_btn');
+
+ expect(screen.queryByText('Adjust the amount')).toBeInTheDocument();
+ expect(screen.queryByText('Guest Developer')).toBeInTheDocument();
+ expect(screen.queryByTestId('USDText')).toBeInTheDocument();
+ expect(screen.queryByText('SAT')).toBeInTheDocument();
+ });
+
+ it('Test that increment and decrement button on input field for amount works correctly', async () => {
+ const props: CodingBountiesProps = {
+ ...defaultProps,
+ paid: false
+ };
+
+ uiStore.setMeInfo({
+ ...user,
+ owner_alias: props.person.owner_alias
+ });
+
+ render();
+
+ const inputSAT = screen.getByTestId('input_sats');
+
+ expect(inputSAT).toBeInTheDocument();
+
+ fireEvent.change(inputSAT, { target: { value: 100 } });
+
+ await waitFor(() => expect(inputSAT).toHaveValue(100), { timeout: 3000 });
+ });
+
+ it('Test that clicking on next button takes you to Award Badge section assert all the necessary ui elements', async () => {
+ const props: CodingBountiesProps = {
+ ...defaultProps,
+ creatorStep: 2,
+ paid: false
+ };
+
+ uiStore.setMeInfo({
+ ...user,
+ owner_alias: props.person.owner_alias
+ });
+
+ render();
+ expect(screen.getByText('Mark as Paid')).toBeInTheDocument();
+ expect(screen.queryByText('Award Badge')).toBeInTheDocument();
+ expect(screen.getAllByTestId('check_box').length).toBe(2);
+ });
+
+ it('Test that button state and text changes according to selection in Award Badge Section', () => {
+ const props: CodingBountiesProps = {
+ ...defaultProps,
+ creatorStep: 2,
+ paid: false
+ };
+
+ uiStore.setMeInfo({
+ ...user,
+ owner_alias: props.person.owner_alias
+ });
+
+ render();
+
+ const checkBox = screen.getAllByTestId('check_box');
+
+ fireEvent.click(checkBox[0]);
+
+ (async () => {
+ await waitFor(() => {
+ expect(screen.getByText('Mark as Paid'));
+ });
+ })();
+ });
+
+ it('test that the mark as paid button updates the bounty paid state', async () => {
+ const props: CodingBountiesProps = {
+ ...defaultProps,
+ creatorStep: 0,
+ paid: false,
+ isAssigned: true
+ };
+
+ uiStore.setMeInfo({
+ ...user,
+ owner_alias: props.person.owner_alias
+ });
+
+ const { container } = render();
+
+ const markAsPaid = screen.getByText('Mark as Paid');
+ fireEvent.click(markAsPaid);
+
+ (async () => {
+ await waitFor(() => expect(screen.getByText('complete')));
+ })();
+ });
});
diff --git a/src/people/widgetViews/wantedViews/DesktopView.tsx b/src/people/widgetViews/wantedViews/DesktopView.tsx
index 0da1bc40f..e3365d5cf 100644
--- a/src/people/widgetViews/wantedViews/DesktopView.tsx
+++ b/src/people/widgetViews/wantedViews/DesktopView.tsx
@@ -70,7 +70,7 @@ function DesktopView(props: WantedViewsProps) {
diff --git a/src/people/widgetViews/wantedViews/MobileView.tsx b/src/people/widgetViews/wantedViews/MobileView.tsx
index 43ba922de..9b2fb975b 100644
--- a/src/people/widgetViews/wantedViews/MobileView.tsx
+++ b/src/people/widgetViews/wantedViews/MobileView.tsx
@@ -77,7 +77,7 @@ function MobileView(props: any) {
{
+ const bounty = { ...bountyDetails.bounty };
+ let assignee;
+ let organization;
+ const owner = { ...bountyDetails.owner };
+
+ if (bounty.assignee) {
+ assignee = { ...bountyDetails.assignee };
+ }
+ if (bounty.org_uuid) {
+ organization = { ...bountyDetails.organization };
+ }
+ return {
+ body: { ...bounty, assignee: assignee || '' },
+ person: { ...owner, wanteds: [] } || { wanteds: [] },
+ organization: { ...organization }
+ };
+};
diff --git a/src/store/main.ts b/src/store/main.ts
index d20f0aa0f..9597fe44a 100644
--- a/src/store/main.ts
+++ b/src/store/main.ts
@@ -61,6 +61,7 @@ export interface Person {
id: number;
unique_name: string;
owner_pubkey: string;
+ uuid: string;
owner_alias: string;
description: string;
img: string;
@@ -309,6 +310,12 @@ export const defaultOrgBountyStatus: OrgBountyStatus = {
Completed: false
};
+export const defaultSuperAdminBountyStatus: BountyStatus = {
+ Open: false,
+ Assigned: false,
+ Paid: false
+};
+
export class MainStore {
[x: string]: any;
tribes: Tribe[] = [];
@@ -910,7 +917,7 @@ export class MainStore {
ps3,
(n: any) => uiStore.setPeopleBountiesPageNumber(n),
queryParams,
- 'wanted'
+ 'bounties'
);
this.setPeopleBounties(wanteds);
}
@@ -927,17 +934,13 @@ export class MainStore {
this.personAssignedBounties = bounties;
}
- async getPersonAssignedBounties(queryParams?: any, pubkey?: string): Promise {
+ async getPersonAssignedBounties(queryParams?: any, uuid?: string): Promise {
queryParams = { ...queryParams, search: uiStore.searchText };
- const query = this.appendQueryParams(
- `people/wanteds/assigned/${pubkey}`,
- paginationQueryLimit,
- {
- sortBy: 'paid',
- ...queryParams
- }
- );
+ const query = this.appendQueryParams(`people/wanteds/assigned/${uuid}`, paginationQueryLimit, {
+ sortBy: 'paid',
+ ...queryParams
+ });
try {
const ps2 = await api.get(query);
@@ -978,10 +981,10 @@ export class MainStore {
this.createdBounties = bounties;
}
- async getPersonCreatedBounties(queryParams?: any, pubkey?: string): Promise {
+ async getPersonCreatedBounties(queryParams?: any, uuid?: string): Promise {
queryParams = { ...queryParams, search: uiStore.searchText };
- const query = this.appendQueryParams(`people/wanteds/created/${pubkey}`, paginationQueryLimit, {
+ const query = this.appendQueryParams(`people/wanteds/created/${uuid}`, paginationQueryLimit, {
...queryParams,
sortBy: 'paid'
});
@@ -1162,7 +1165,7 @@ export class MainStore {
ps3,
(n: any) => uiStore.setPeopleBountiesPageNumber(n),
queryParams,
- 'wanted'
+ 'bounties'
);
this.setPeopleBounties(wanteds);
@@ -1214,7 +1217,7 @@ export class MainStore {
ps3,
(n: any) => uiStore.setPeopleBountiesPageNumber(n),
queryParams,
- 'wanted'
+ 'bounties'
);
this.setPeopleBounties(wanteds);
@@ -1287,7 +1290,7 @@ export class MainStore {
ps3,
(n: any) => uiStore.setPeopleBountiesPageNumber(n),
queryParams,
- 'wanted'
+ 'bounties'
);
this.setPeopleBounties(wanteds);
@@ -1381,7 +1384,7 @@ export class MainStore {
const l = [...currentList, ...newList];
const set = new Set();
- if (type === 'wanted') {
+ if (type === 'bounties') {
const uniqueArray = l.filter((item: any) => {
if (item.body && item.body.id && !set.has(item.body.id)) {
set.add(item.body.id);
@@ -1410,7 +1413,7 @@ export class MainStore {
}
setActivePerson(p: Person) {
- this.activePerson = [p];
+ this._activePerson = [p];
}
@memo()
@@ -1419,6 +1422,12 @@ export class MainStore {
return p;
}
+ @memo()
+ async getPersonByUuid(uuid: string): Promise {
+ const p = await api.get(`person/uuid/${uuid}`);
+ return p;
+ }
+
async getPersonById(id: number): Promise {
const p = await api.get(`person/id/${id}`);
this.setActivePerson(p);
| |