Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions jest.setup.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@testing-library/jest-dom/extend-expect';
82 changes: 82 additions & 0 deletions spec/Components/CioAutocomplete/CioAutocomplete.server.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* eslint-disable no-console */
import '@testing-library/jest-dom';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { CioAutocomplete } from '../../../src';
import { mockCioClientJS } from '../../test-utils';
import { apiKey as DEMO_API_KEY } from '../../../src/constants';

describe('CioAutocomplete Server-Side Rendering', () => {
beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation(() => {});
});

afterEach(() => {
jest.restoreAllMocks();
});

afterAll(() => {
jest.resetAllMocks();
});

it('Does not throw an error when provided API key (JS CioClient is null)', () => {
expect(() => {
// @ts-ignore
ReactDOMServer.renderToString(<CioAutocomplete apiKey={DEMO_API_KEY} />);
}).not.toThrow();

expect(console.error).not.toHaveBeenCalled();
});

it('Throws an error when passed a JS CioClient without clientId and sessionId', () => {
expect(() => {
ReactDOMServer.renderToString(
<CioAutocomplete cioJsClient={mockCioClientJS()} onSubmit={() => {}} />
);
}).toThrow('sessionId is a required user parameter of type number');
});

it('Throws an error when passed a JS CioClient without clientId', () => {
expect(() => {
ReactDOMServer.renderToString(
<CioAutocomplete cioJsClient={mockCioClientJS({ sessionId: 1 })} onSubmit={() => {}} />
);
}).toThrow('clientId is a required user parameter of type string');
});

it('Does not throw an error when passed a JS CioClient with clientId and sessionId', () => {
expect(() => {
ReactDOMServer.renderToString(
<CioAutocomplete
cioJsClient={mockCioClientJS({ sessionId: 1, clientId: '1' })}
onSubmit={() => {}}
/>
);
}).not.toThrow();

expect(console.error).not.toHaveBeenCalled();
});

it('Render custom placeholder when passed as a prop', () => {
const html = ReactDOMServer.renderToString(
<CioAutocomplete
apiKey={DEMO_API_KEY}
placeholder='Custom placeholder text'
onSubmit={() => {}}
/>
);

expect(html).toContain('Custom placeholder text');
});

it('Accepts custom styles when passed as a prop', () => {
const html = ReactDOMServer.renderToString(
<CioAutocomplete
apiKey={DEMO_API_KEY}
autocompleteClassName='cio-autocomplete custom-autocomplete-styles'
onSubmit={() => {}}
/>
);
expect(html).toContain('custom-autocomplete-styles');
});
});
108 changes: 107 additions & 1 deletion spec/Components/CioAutocomplete/CioAutocomplete.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
/* eslint-disable no-console */
import '@testing-library/jest-dom';
import React from 'react';
import { render } from '@testing-library/react';
import { CioAutocomplete } from '../../../src';
import { mockCioClientJS } from '../../test-utils';
import { apiKey as DEMO_API_KEY, onSubmitDefault as onSubmit } from '../../../src/constants';

describe('CioAutocomplete React Client-Side Rendering', () => {
describe('CioAutocomplete Client-Side Rendering', () => {
beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation(() => {});
});
Expand All @@ -12,6 +15,10 @@ describe('CioAutocomplete React Client-Side Rendering', () => {
jest.restoreAllMocks();
});

afterAll(() => {
jest.resetAllMocks();
});

it("Logs an error if apiKey or cioJsClient isn't provided", () => {
// @ts-ignore
render(<CioAutocomplete />);
Expand All @@ -20,4 +27,103 @@ describe('CioAutocomplete React Client-Side Rendering', () => {
expect.stringContaining('Either apiKey or cioJsClient is required')
);
});

it("Doesn't throw error if we provide an API key", () => {
expect(() => {
render(<CioAutocomplete apiKey={DEMO_API_KEY} onSubmit={() => {}} />);
}).not.toThrow();

expect(console.error).not.toHaveBeenCalled();
});

it("Doesn't throw error if we provide a ConstructorIO Client", () => {
expect(() => {
render(<CioAutocomplete cioJsClient={mockCioClientJS()} onSubmit={() => {}} />);
}).not.toThrow();

expect(console.error).not.toHaveBeenCalled();
});

it("Doesn't throw error if we provide a CIOClientOptions", () => {
const cioJsClientOptions = { apiKey: DEMO_API_KEY, serviceUrl: 'https://ac.cnstrc.com' };

expect(() => {
render(
<CioAutocomplete
apiKey={DEMO_API_KEY}
cioJsClientOptions={cioJsClientOptions}
onSubmit={() => {}}
/>
);
}).not.toThrow();

expect(console.error).not.toHaveBeenCalled();
});

it('Render custom placeholder when passed as a prop', () => {
const { getByPlaceholderText } = render(
<CioAutocomplete
apiKey={DEMO_API_KEY}
placeholder='Custom placeholder text'
onSubmit={() => {}}
/>
);

expect(getByPlaceholderText('Custom placeholder text')).toBeInTheDocument();
});

it('Accepts custom styles when passed as a prop', () => {
const { container } = render(
<CioAutocomplete
apiKey={DEMO_API_KEY}
autocompleteClassName='cio-autocomplete custom-autocomplete-styles'
onSubmit={() => {}}
/>
);

expect(container.querySelector('.custom-autocomplete-styles')).toBeInTheDocument();
});

it("Fully featured example doesn't throw an error", () => {
enum Type {
recommendations = 'recommendations',
}

const props = {
cioJsClient: mockCioClientJS(),
onSubmit,
autocompleteClassName: '',
placeholder: 'What can we help you find?',
advancedParameters: {
displaySearchSuggestionImages: true,
displaySearchSuggestionResultCounts: true,
numTermsWithGroupSuggestions: 6,
},
sections: [
{
indexSectionName: 'Search Suggestions',
numResults: 8,
displaySearchTermHighlights: true,
},
{
indexSectionName: 'Products',
numResults: 6,
displaySearchTermHighlights: true,
},
],
zeroStateSections: [
{
podId: 'bestsellers',
type: Type.recommendations,
numResults: 6,
},
],
};

expect(() => {
render(<CioAutocomplete {...props} />);
}).not.toThrow();

expect(console.error).not.toHaveBeenCalled();
});
});
36 changes: 36 additions & 0 deletions spec/local_examples/apiAutocompleteResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"sections": {
"Products": [
{
"matched_terms": [],
"labels": {

},
"data": {
"id": "123",
"url": "example.com",
"image_url": "example.png",
"description": ""
},
"value": "item"
}
],
"Search Suggestions": [
{
"matched_terms": [],
"labels": {

},
"value": "suggestion"
}
]
},
"total_num_results_per_section": {
"Products": 1,
"Search Suggestions": 1
},
"result_id": "123",
"request": {
"term": "red"
}
}
25 changes: 25 additions & 0 deletions spec/local_examples/apiRecommendationsResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"request": {
"section": "Products",
"num_results": 1,
"pod_id": "bestsellers"
},
"response": {
"results": [
{
"data": {
"id": "123",
"url": "no-url",
"description": "description",
"price": 99,
"image_url": "example.jpg"
},
"value": "product",
"strategy": { "id": "bestsellers" }
}
],
"total_num_results": 1,
"pod": { "id": "bestsellers", "display_name": "Best Sellers" }
},
"result_id": "123"
}
29 changes: 29 additions & 0 deletions spec/test-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* eslint-disable import/prefer-default-export */
import ConstructorIO from '@constructor-io/constructorio-client-javascript';
import { apiKey as DEMO_API_KEY } from '../src/constants';
import apiAutocompleteResponse from './local_examples/apiAutocompleteResponse.json';
import apiRecommendationsResponse from './local_examples/apiRecommendationsResponse.json';

// ConstructorIO Client Mock
class MockConstructorIO extends ConstructorIO {
autocomplete = {
getAutocompleteResults: jest.fn().mockResolvedValue(apiAutocompleteResponse),
} as any;

recommendations = {
getRecommendations: jest.fn().mockResolvedValue(apiRecommendationsResponse),
} as any;

// Override other methods as needed
}

const mockCioClientJS = (params?) => new MockConstructorIO({ apiKey: DEMO_API_KEY, ...params });

const mockUseCioClient = typeof window !== 'undefined' ? mockCioClientJS() : null;

jest.mock('../src/hooks/useCioClient', () => ({
__esModule: true,
default: () => (() => mockUseCioClient)(),
}));

export { mockCioClientJS };
2 changes: 1 addition & 1 deletion tsconfig.eslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"compilerOptions": {
"types": ["jest"]
},
"include": ["src", "tests", "spec"]
"include": ["src", "tests", "spec", "jest.setup.d.ts"]
}
7 changes: 5 additions & 2 deletions tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"esModuleInterop": true
"esModuleInterop": true,
"types": ["node", "jest", "@testing-library/jest-dom"],
"jsx": "react",
"resolveJsonModule": true,
},
"include": ["spec/**/*.ts", "spec/**/*.tsx"]
"include": ["spec/**/*.ts", "spec/**/*.tsx", "jest.setup.d.ts"]
}