diff --git a/src/resolvers/projectResolver.test.ts b/src/resolvers/projectResolver.test.ts index c778e84eb..5138d9554 100644 --- a/src/resolvers/projectResolver.test.ts +++ b/src/resolvers/projectResolver.test.ts @@ -26,6 +26,7 @@ import { fetchFeaturedProjectUpdate, fetchLatestProjectUpdates, fetchLikedProjectsQuery, + fetchMultiFilterAllProjectsQuery, fetchNewProjectsPerDate, fetchProjectBySlugQuery, fetchProjectUpdatesQuery, @@ -159,7 +160,7 @@ describe( similarProjectsBySlugTestCases, ); -// describe('projectSearch test cases --->', projectSearchTestCases); +describe('projectSearch test cases --->', projectSearchTestCases); describe('projectUpdates query test cases --->', projectUpdatesTestCases); @@ -4203,49 +4204,49 @@ function projectUpdatesTestCases() { }); } -// function projectSearchTestCases() { -// it('should return projects with a typo in the end of searchTerm', async () => { -// const limit = 1; -// const USER_DATA = SEED_DATA.FIRST_USER; -// const result = await axios.post(graphqlUrl, { -// query: fetchMultiFilterAllProjectsQuery, -// variables: { -// limit, -// // Typo in the title -// searchTerm: SEED_DATA.SECOND_PROJECT.title.slice(0, -1) + 'a', -// connectedWalletUserId: USER_DATA.id, -// }, -// }); - -// const projects = result.data.data.allProjects.projects; -// assert.equal(projects.length, limit); -// assert.equal(projects[0].title, SEED_DATA.SECOND_PROJECT.title); -// assert.equal(projects[0].slug, SEED_DATA.SECOND_PROJECT.slug); -// assert.equal(projects[0].id, SEED_DATA.SECOND_PROJECT.id); -// }); - -// it('should return projects with the project title inverted in the searchTerm', async () => { -// const limit = 1; -// const USER_DATA = SEED_DATA.FIRST_USER; -// const result = await axios.post(graphqlUrl, { -// query: fetchMultiFilterAllProjectsQuery, -// variables: { -// limit, -// searchTerm: SEED_DATA.SECOND_PROJECT.title -// .split(' ') -// .reverse() -// .join(' '), -// connectedWalletUserId: USER_DATA.id, -// }, -// }); - -// const projects = result.data.data.allProjects.projects; -// assert.equal(projects.length, limit); -// assert.equal(projects[0].title, SEED_DATA.SECOND_PROJECT.title); -// assert.equal(projects[0].slug, SEED_DATA.SECOND_PROJECT.slug); -// assert.equal(projects[0].id, SEED_DATA.SECOND_PROJECT.id); -// }); -// } +function projectSearchTestCases() { + it('should return projects with a typo in the end of searchTerm', async () => { + const limit = 1; + const USER_DATA = SEED_DATA.FIRST_USER; + const result = await axios.post(graphqlUrl, { + query: fetchMultiFilterAllProjectsQuery, + variables: { + limit, + // Typo in the title + searchTerm: SEED_DATA.SECOND_PROJECT.title.slice(0, -1) + 'a', + connectedWalletUserId: USER_DATA.id, + }, + }); + + const projects = result.data.data.allProjects.projects; + assert.equal(projects.length, limit); + assert.equal(projects[0].title, SEED_DATA.SECOND_PROJECT.title); + assert.equal(projects[0].slug, SEED_DATA.SECOND_PROJECT.slug); + assert.equal(projects[0].id, SEED_DATA.SECOND_PROJECT.id); + }); + + it('should return projects with the project title inverted in the searchTerm', async () => { + const limit = 1; + const USER_DATA = SEED_DATA.FIRST_USER; + const result = await axios.post(graphqlUrl, { + query: fetchMultiFilterAllProjectsQuery, + variables: { + limit, + searchTerm: SEED_DATA.SECOND_PROJECT.title + .split(' ') + .reverse() + .join(' '), + connectedWalletUserId: USER_DATA.id, + }, + }); + + const projects = result.data.data.allProjects.projects; + assert.equal(projects.length, limit); + assert.equal(projects[0].title, SEED_DATA.SECOND_PROJECT.title); + assert.equal(projects[0].slug, SEED_DATA.SECOND_PROJECT.slug); + assert.equal(projects[0].id, SEED_DATA.SECOND_PROJECT.id); + }); +} function getProjectUpdatesTestCases() { it('should return project updates with current take', async () => { diff --git a/src/resolvers/projectResolver.ts b/src/resolvers/projectResolver.ts index 4e5c60276..ad2f6726f 100644 --- a/src/resolvers/projectResolver.ts +++ b/src/resolvers/projectResolver.ts @@ -40,6 +40,7 @@ import { Donation } from '../entities/donation'; import { ProjectImage } from '../entities/projectImage'; import { ApolloContext } from '../types/ApolloContext'; import { publicSelectionFields, User } from '../entities/user'; +import config from '../config'; import { Context } from '../context'; import SentryLogger from '../sentryLogger'; import { @@ -318,21 +319,39 @@ export class ProjectResolver { searchTerm?: string, ) { if (!searchTerm) return query; + const similarityThreshold = + Number(config.get('PROJECT_SEARCH_SIMILARITY_THRESHOLD')) || 0.4; return query.andWhere( new Brackets(qb => { - qb.where('project.title ILIKE :searchTerm', { - searchTerm: `%${searchTerm}%`, - }) - .orWhere('project.description ILIKE :searchTerm', { - searchTerm: `%${searchTerm}%`, - }) - .orWhere('project.impactLocation ILIKE :searchTerm', { - searchTerm: `%${searchTerm}%`, - }) - .orWhere('user.name ILIKE :searchTerm', { + qb.where( + 'WORD_SIMILARITY(project.title, :searchTerm) > :similarityThreshold', + { searchTerm: `%${searchTerm}%`, - }); + similarityThreshold, + }, + ) + .orWhere( + 'WORD_SIMILARITY(project.description, :searchTerm) > :similarityThreshold', + { + searchTerm: `%${searchTerm}%`, + similarityThreshold, + }, + ) + .orWhere( + 'WORD_SIMILARITY(project.impactLocation, :searchTerm) > :similarityThreshold', + { + searchTerm: `%${searchTerm}%`, + similarityThreshold, + }, + ) + .orWhere( + 'WORD_SIMILARITY(user.name, :searchTerm) > :similarityThreshold', + { + searchTerm: `%${searchTerm}%`, + similarityThreshold, + }, + ); }), ); } diff --git a/src/server/adminJs/adminJsPermissions.test.ts b/src/server/adminJs/adminJsPermissions.test.ts index ab53a39ba..aff23d379 100644 --- a/src/server/adminJs/adminJsPermissions.test.ts +++ b/src/server/adminJs/adminJsPermissions.test.ts @@ -275,9 +275,7 @@ describe.skip('canAccessUserAction test cases', () => { roles.forEach(role => { Object.keys(actionsPerRole[role]).forEach(page => { actions.forEach(action => { - it(`should return ${actionsPerRole[role][page].includes( - action, - )} for ${role} --> ${action} on ${page}`, function () { + it(`should return ${actionsPerRole[role][page].includes(action)} for ${role} --> ${action} on ${page}`, function () { assert.strictEqual( canAccessAction(role, page, action), actionsPerRole[role][page].includes(action), diff --git a/src/services/recurringDonationService.ts b/src/services/recurringDonationService.ts index 351219943..537fc55e8 100644 --- a/src/services/recurringDonationService.ts +++ b/src/services/recurringDonationService.ts @@ -174,9 +174,7 @@ export const createRelatedDonationsToStream = async ( const toAddress = projectRelatedAddress?.address?.toLowerCase(); const fromAddress = donorUser.walletAddress?.toLowerCase(); - const transactionTx = `${streamData.id?.toLowerCase()}-${ - streamPeriod.endTime - }`; + const transactionTx = `${streamData.id?.toLowerCase()}-${streamPeriod.endTime}`; const donation = Donation.create({ amount: normalizeNegativeAmount( streamPeriod.amount,