diff --git a/jest.config.js b/jest.config.js index 59be9a7f..7e86a104 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,20 +6,38 @@ module.exports = { ], projects: [ { + globals: { + 'ts-jest': { + tsconfig: 'tsconfig.test.json', + }, + }, preset: 'ts-jest', displayName: 'dom', // We are using jest-environment-jsdom 25 until we stop supporting node 10 // jest-environment-jsdom 25 uses jsdom 15 which still supports node 10 testEnvironment: 'jest-environment-jsdom', - testPathIgnorePatterns: ['/tests/env', '/playgrounds'], - testMatch: ['**/*.tests.ts'], + testPathIgnorePatterns: [ + '/tests/env', + '/playgrounds', + '/tests/assets', + ], + testMatch: ['**/*.tests.ts', '/tests/**/*.ts'], }, { + globals: { + 'ts-jest': { + tsconfig: 'tsconfig.test.json', + }, + }, preset: 'ts-jest', displayName: 'node', testEnvironment: 'node', - testPathIgnorePatterns: ['/tests/env', '/playgrounds'], - testMatch: ['**/*.tests.ts'], + testPathIgnorePatterns: [ + '/tests/env', + '/playgrounds', + '/tests/assets', + ], + testMatch: ['**/*.tests.ts', '/tests/**/*.ts'], }, ], } diff --git a/src/client/index.ts b/src/client/index.ts index 4e9e84d5..84476991 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -45,12 +45,13 @@ export function instantMeiliSearch( .search(msSearchParams.q, msSearchParams) // Parses the MeiliSearch response and returns it for InstantSearch - return transformToISResponse( + const ISresponse = transformToISResponse( indexUid, searchResponse, instantSearchParams, context ) + return ISresponse } catch (e) { console.error(e) throw new Error(e) diff --git a/src/transformers/to-instantsearch-hits.ts b/src/transformers/to-instantsearch-hits.ts index 321f3741..e88cfa3d 100644 --- a/src/transformers/to-instantsearch-hits.ts +++ b/src/transformers/to-instantsearch-hits.ts @@ -14,20 +14,22 @@ export const transformToISHits: TransformToISHitsm = function ( const paginatedHits = paginateHits(meiliSearchHits, instantMeiliSearchContext) return paginatedHits.map((hit: any) => { - const { _formatted: formattedHit, ...restOfHit } = hit - // Creates Hit object compliant with InstantSearch - return { - ...restOfHit, - _highlightResult: createHighlighResult({ - formattedHit, - ...instantSearchParams, - }), - _snippetResult: createSnippetResult({ - formattedHit, - ...instantSearchParams, - }), - ...(primaryKey && { objectID: hit[primaryKey] }), + if (Object.keys(hit).length > 0) { + const { _formatted: formattedHit, _matchesInfo, ...restOfHit } = hit + return { + ...restOfHit, + _highlightResult: createHighlighResult({ + formattedHit, + ...instantSearchParams, + }), + _snippetResult: createSnippetResult({ + formattedHit, + ...instantSearchParams, + }), + ...(primaryKey && { objectID: hit[primaryKey] }), + } } + return hit }) } diff --git a/src/types/instantsearch-types.ts b/src/types/instantsearch-types.ts index 688b86e2..a602266e 100644 --- a/src/types/instantsearch-types.ts +++ b/src/types/instantsearch-types.ts @@ -94,7 +94,8 @@ export type SearchRequest = { params: SearchRequestParameters } -export type Hit = { +export type Hit = T & { + [key: string]: any _highlightResult?: object } diff --git a/tests/assets/utils.ts b/tests/assets/utils.ts new file mode 100644 index 00000000..2ca669fa --- /dev/null +++ b/tests/assets/utils.ts @@ -0,0 +1,57 @@ +import { instantMeiliSearch } from '../../src' + +const dataset = [ + { + id: 2, + title: 'Ariel', + overview: + "Taisto Kasurinen is a Finnish coal miner whose father has just committed suicide and who is framed for a crime he did not commit. In jail, he starts to dream about leaving the country and starting a new life. He escapes from prison but things don't go as planned...", + genres: ['Drama', 'Crime', 'Comedy'], + poster: 'https://image.tmdb.org/t/p/w500/ojDg0PGvs6R9xYFodRct2kdI6wC.jpg', + release_date: 593395200, + }, + { + id: 5, + title: 'Four Rooms', + overview: + "It's Ted the Bellhop's first night on the job...and the hotel's very unusual guests are about to place him in some outrageous predicaments. It seems that this evening's room service is serving up one unbelievable happening after another.", + genres: ['Crime', 'Comedy'], + poster: 'https://image.tmdb.org/t/p/w500/75aHn1NOYXh4M7L5shoeQ6NGykP.jpg', + release_date: 818467200, + }, + { + id: 6, + title: 'Judgment Night', + overview: + 'While racing to a boxing match, Frank, Mike, John and Rey get more than they bargained for. A wrong turn lands them directly in the path of Fallon, a vicious, wise-cracking drug lord. After accidentally witnessing Fallon murder a disloyal henchman, the four become his unwilling prey in a savage game of cat & mouse as they are mercilessly stalked through the urban jungle in this taut suspense drama', + genres: ['Action', 'Thriller', 'Crime'], + poster: 'https://image.tmdb.org/t/p/w500/rYFAvSPlQUCebayLcxyK79yvtvV.jpg', + release_date: 750643200, + }, + { + id: 11, + title: 'Star Wars', + overview: + 'Princess Leia is captured and held hostage by the evil Imperial forces in their effort to take over the galactic Empire. Venturesome Luke Skywalker and dashing captain Han Solo team together with the loveable robot duo R2-D2 and C-3PO to rescue the beautiful princess and restore peace and justice in the Empire.', + genres: ['Adventure', 'Action', 'Science Fiction'], + poster: 'https://image.tmdb.org/t/p/w500/6FfCtAuVAW8XJjZ7eWeLibRLWTw.jpg', + release_date: 233366400, + }, +] + +export type Movies = { + id?: number + title?: string + overview?: string + genres?: string[] + release_date?: number // eslint-disable-line + _highlightResult?: Movies +} + +const searchClient = instantMeiliSearch('http://localhost:7700', 'masterKey') +const wrongSearchClient = instantMeiliSearch( + 'http://localhost:7777', + 'masterKey' +) + +export { searchClient, dataset, wrongSearchClient } diff --git a/tests/configure.attributes-to-retrieve.tests.ts b/tests/configure.attributes-to-retrieve.tests.ts new file mode 100644 index 00000000..2197d92e --- /dev/null +++ b/tests/configure.attributes-to-retrieve.tests.ts @@ -0,0 +1,168 @@ +import { searchClient, dataset, Movies } from './assets/utils' + +describe('Instant MeiliSearch Browser test', () => { + beforeAll(async () => { + try { + await searchClient.MeiliSearchClient.deleteIndex('movies') + } catch (e) { + // movies does not exist + } + const moviesUpdate = await searchClient.MeiliSearchClient.index( + 'movies' + ).addDocuments(dataset) + await searchClient.MeiliSearchClient.index('movies').waitForPendingUpdate( + moviesUpdate.updateId + ) + }) + + test('Test attributesToRetrieve on no attributes', async () => { + const response = await searchClient.search([ + { + indexName: 'movies', + params: { + attributesToRetrieve: [], + query: 'ariel', + }, + }, + ]) + const notRetrieved = [ + 'id', + 'overview', + 'genres', + 'poster', + 'release_date', + 'title', + '_highlightResult', + ] + const hit = response.results[0].hits[0] + notRetrieved.map((attribute: string) => + expect(hit[attribute]).not.toBeDefined() + ) + }) + + test('Test attributesToRetrieve on null', async () => { + const response = await searchClient.search([ + { + indexName: 'movies', + params: { + attributesToRetrieve: [], + query: 'ariel', + }, + }, + ]) + const notRetrieved = [ + 'id', + 'overview', + 'genres', + 'poster', + 'release_date', + 'title', + '_highlightResult', + ] + const hit = response.results[0].hits[0] + notRetrieved.map((attribute: string) => + expect(hit[attribute]).not.toBeDefined() + ) + }) + + test('Test attributesToRetrieve on one non existing attribute', async () => { + const response = await searchClient.search([ + { + indexName: 'movies', + params: { + attributesToRetrieve: ['test'], + query: 'ariel', + }, + }, + ]) + const notRetrieved = [ + 'id', + 'overview', + 'genres', + 'poster', + 'release_date', + 'title', + '_highlightResult', + ] + const hit = response.results[0].hits[0] + notRetrieved.map( + (attribute: string) => + hit._highlightResult && + expect(hit._highlightResult[attribute]).toBeDefined() + ) + }) + + test('Test attributesToRetrieve on one existing attribute', async () => { + const response = await searchClient.search([ + { + indexName: 'movies', + params: { + attributesToRetrieve: ['title'], + query: 'ariel', + }, + }, + ]) + const notRetrieved = ['id', 'overview', 'genres', 'poster', 'release_date'] + const hit = response.results[0].hits[0] + expect(hit.title).toEqual('Ariel') + notRetrieved.map((attribute: string) => + expect(hit[attribute]).not.toBeDefined() + ) + notRetrieved.map( + (attribute: string) => + hit._highlightResult && + expect(hit._highlightResult[attribute]).not.toBeDefined() + ) + }) + + test('Test attributesToRetrieve on default value', async () => { + const response = await searchClient.search([ + { + indexName: 'movies', + params: { + query: 'ariel', + }, + }, + ]) + const notRetrieved = ['id', 'overview', 'genres', 'poster', 'release_date'] + const hit = response.results[0].hits[0] + expect(hit.title).toEqual('Ariel') + expect(hit._highlightResult).toBeDefined() + notRetrieved.map((attribute: string) => + expect(hit[attribute]).toBeDefined() + ) + notRetrieved.map( + (attribute: string) => + hit._highlightResult && + expect(hit._highlightResult[attribute]).toBeDefined() + ) + }) + + test('Test attributesToRetrieve on wild card', async () => { + const response = await searchClient.search([ + { + indexName: 'movies', + params: { + query: 'ariel', + attributesToRetrieve: ['*'], + }, + }, + ]) + const retrieved = [ + 'id', + 'overview', + 'genres', + 'poster', + 'release_date', + 'title', + ] + const hit = response.results[0].hits[0] + expect(hit.title).toEqual('Ariel') + retrieved.map((attribute: string) => expect(hit[attribute]).toBeDefined()) + retrieved.map( + (attribute: string) => + hit._highlightResult && + expect(hit._highlightResult[attribute]).toBeDefined() + ) + }) +}) diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 00000000..788f7d05 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "suppressImplicitAnyIndexErrors": true, + } +}