diff --git a/packages/quantic/force-app/main/default/lwc/quanticCategoryFacet/__tests__/quanticCategoryFacet.test.js b/packages/quantic/force-app/main/default/lwc/quanticCategoryFacet/__tests__/quanticCategoryFacet.test.js index 7f22ca11d4d..98e4f242d57 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticCategoryFacet/__tests__/quanticCategoryFacet.test.js +++ b/packages/quantic/force-app/main/default/lwc/quanticCategoryFacet/__tests__/quanticCategoryFacet.test.js @@ -15,16 +15,21 @@ jest.mock('c/quanticUtils', () => ({ CATEGORYFACETS: 'categoryFacets', }, }, + I18nUtils: { + format: jest.fn(), + }, })); jest.mock('c/quanticHeadlessLoader'); const selectors = { facetContent: '[data-test="facet-content"]', + componentError: 'c-quantic-component-error', }; const exampleFacetId = 'example facet id'; +const exampleField = 'exampleField'; const defaultOptions = { - field: 'example field', + field: exampleField, }; const categoryFacetControllerMock = { subscribe: jest.fn((callback) => callback()), @@ -33,6 +38,8 @@ const categoryFacetControllerMock = { values: [], }, }; +const parentFacetIdError = `The ${exampleField} c-quantic-category-facet requires dependsOn.parentFacetId to be a valid string.`; +const expectedValueError = `The ${exampleField} c-quantic-category-facet requires dependsOn.expectedValue to be a valid string.`; function createTestComponent(options = defaultOptions) { prepareHeadlessState(); @@ -82,6 +89,21 @@ const exampleEngine = { }; let isInitialized = false; +function mockBueno() { + // @ts-ignore + mockHeadlessLoader.getBueno = () => { + // @ts-ignore + global.Bueno = { + isString: jest + .fn() + .mockImplementation( + (value) => Object.prototype.toString.call(value) === '[object String]' + ), + }; + return new Promise((resolve) => resolve()); + }; +} + function mockSuccessfulHeadlessInitialization() { // @ts-ignore mockHeadlessLoader.initializeWithHeadless = (element, _, initialize) => { @@ -104,6 +126,7 @@ function cleanup() { describe('c-quantic-category-facet', () => { beforeAll(() => { mockSuccessfulHeadlessInitialization(); + mockBueno(); }); afterEach(() => { @@ -198,6 +221,90 @@ describe('c-quantic-category-facet', () => { }); }); + describe('validation of the dependsOn property', () => { + let consoleError; + beforeAll(() => { + consoleError = jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + describe('when dependsOn.parentFacetId is not provided', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.parentFacetId is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 1, + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.expectedValue is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 'filetype', + expectedValue: 2, + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(expectedValueError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + }); + describe('when the component is disconnected', () => { it('should make the condition manager stop watching the facet', async () => { const exampleFacetDependency = { diff --git a/packages/quantic/force-app/main/default/lwc/quanticCategoryFacet/quanticCategoryFacet.js b/packages/quantic/force-app/main/default/lwc/quanticCategoryFacet/quanticCategoryFacet.js index 25c4f69358f..14672c6ed42 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticCategoryFacet/quanticCategoryFacet.js +++ b/packages/quantic/force-app/main/default/lwc/quanticCategoryFacet/quanticCategoryFacet.js @@ -14,6 +14,7 @@ import { initializeWithHeadless, registerToStore, getHeadlessBundle, + getBueno, } from 'c/quanticHeadlessLoader'; import { I18nUtils, @@ -301,6 +302,7 @@ export default class QuanticCategoryFacet extends LightningElement { }, }); if (this.dependsOn) { + this.validateDependsOnProperty(); this.initFacetConditionManager(engine); } this.unsubscribe = this.facet.subscribe(() => this.updateState()); @@ -330,6 +332,26 @@ export default class QuanticCategoryFacet extends LightningElement { this.dispatchEvent(renderFacetEvent); } + validateDependsOnProperty() { + if (this.dependsOn) { + getBueno(this).then(() => { + const {parentFacetId, expectedValue} = this.dependsOn; + if (!Bueno.isString(parentFacetId)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.parentFacetId to be a valid string.` + ); + this.setInitializationError(); + } + if (expectedValue && !Bueno.isString(expectedValue)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.expectedValue to be a valid string.` + ); + this.setInitializationError(); + } + }); + } + } + initFacetConditionManager(engine) { this.categoryFacetConditionsManager = this.headless.buildFacetConditionsManager(engine, { diff --git a/packages/quantic/force-app/main/default/lwc/quanticDateFacet/__tests__/quanticDateFacet.test.js b/packages/quantic/force-app/main/default/lwc/quanticDateFacet/__tests__/quanticDateFacet.test.js index f50f1d8885a..c33dbc5c0dc 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticDateFacet/__tests__/quanticDateFacet.test.js +++ b/packages/quantic/force-app/main/default/lwc/quanticDateFacet/__tests__/quanticDateFacet.test.js @@ -12,16 +12,21 @@ jest.mock('c/quanticUtils', () => ({ DATEFACETS: 'dateFacets', }, }, + I18nUtils: { + format: jest.fn(), + }, })); jest.mock('c/quanticHeadlessLoader'); const selectors = { facetContent: '[data-test="facet-content"]', + componentError: 'c-quantic-component-error', }; const exampleFacetId = 'example facet id'; +const exampleField = 'exampleField'; const defaultOptions = { - field: 'example field', + field: exampleField, }; const dateFacetControllerMock = { subscribe: jest.fn((callback) => callback()), @@ -30,6 +35,8 @@ const dateFacetControllerMock = { values: [], }, }; +const parentFacetIdError = `The ${exampleField} c-quantic-date-facet requires dependsOn.parentFacetId to be a valid string.`; +const expectedValueError = `The ${exampleField} c-quantic-date-facet requires dependsOn.expectedValue to be a valid string.`; function createTestComponent(options = defaultOptions) { prepareHeadlessState(); @@ -79,6 +86,21 @@ const exampleEngine = { }; let isInitialized = false; +function mockBueno() { + // @ts-ignore + mockHeadlessLoader.getBueno = () => { + // @ts-ignore + global.Bueno = { + isString: jest + .fn() + .mockImplementation( + (value) => Object.prototype.toString.call(value) === '[object String]' + ), + }; + return new Promise((resolve) => resolve()); + }; +} + function mockSuccessfulHeadlessInitialization() { // @ts-ignore mockHeadlessLoader.initializeWithHeadless = (element, _, initialize) => { @@ -101,6 +123,7 @@ function cleanup() { describe('c-quantic-date-facet', () => { beforeAll(() => { mockSuccessfulHeadlessInitialization(); + mockBueno(); }); afterEach(() => { @@ -193,6 +216,89 @@ describe('c-quantic-date-facet', () => { }); }); + describe('validation of the dependsOn property', () => { + let consoleError; + beforeAll(() => { + consoleError = jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + describe('when dependsOn.parentFacetId is not provided', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.parentFacetId is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 1, + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.expectedValue is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 'filetype', + expectedValue: 2, + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(expectedValueError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + }); + describe('when the component is disconnected', () => { it('should make the condition manager stop watching the facet', async () => { const exampleFacetDependency = { diff --git a/packages/quantic/force-app/main/default/lwc/quanticDateFacet/quanticDateFacet.js b/packages/quantic/force-app/main/default/lwc/quanticDateFacet/quanticDateFacet.js index 033153d6abe..424766631e3 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticDateFacet/quanticDateFacet.js +++ b/packages/quantic/force-app/main/default/lwc/quanticDateFacet/quanticDateFacet.js @@ -9,6 +9,7 @@ import { initializeWithHeadless, registerToStore, getHeadlessBundle, + getBueno, } from 'c/quanticHeadlessLoader'; import { I18nUtils, @@ -210,6 +211,7 @@ export default class QuanticDateFacet extends LightningElement { element: this.template.host, }); if (this.dependsOn) { + this.validateDependsOnProperty(); this.initFacetConditionManager(engine); } }; @@ -238,6 +240,26 @@ export default class QuanticDateFacet extends LightningElement { this.dispatchEvent(renderFacetEvent); } + validateDependsOnProperty() { + if (this.dependsOn) { + getBueno(this).then(() => { + const {parentFacetId, expectedValue} = this.dependsOn; + if (!Bueno.isString(parentFacetId)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.parentFacetId to be a valid string.` + ); + this.setInitializationError(); + } + if (expectedValue && !Bueno.isString(expectedValue)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.expectedValue to be a valid string.` + ); + this.setInitializationError(); + } + }); + } + } + initFacetConditionManager(engine) { this.dateFacetConditionsManager = this.headless.buildFacetConditionsManager( engine, diff --git a/packages/quantic/force-app/main/default/lwc/quanticFacet/__tests__/quanticFacet.test.js b/packages/quantic/force-app/main/default/lwc/quanticFacet/__tests__/quanticFacet.test.js index de20c1fc18d..4d5adcddc57 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticFacet/__tests__/quanticFacet.test.js +++ b/packages/quantic/force-app/main/default/lwc/quanticFacet/__tests__/quanticFacet.test.js @@ -12,16 +12,21 @@ jest.mock('c/quanticUtils', () => ({ FACETS: 'facets', }, }, + I18nUtils: { + format: jest.fn(), + }, })); jest.mock('c/quanticHeadlessLoader'); const selectors = { facetContent: '[data-test="facet-content"]', + componentError: 'c-quantic-component-error', }; const exampleFacetId = 'example facet id'; +const exampleField = 'exampleField'; const defaultOptions = { - field: 'example field', + field: exampleField, }; const facetControllerMock = { subscribe: jest.fn((callback) => callback()), @@ -30,6 +35,8 @@ const facetControllerMock = { values: [], }, }; +const parentFacetIdError = `The ${exampleField} c-quantic-facet requires dependsOn.parentFacetId to be a valid string.`; +const expectedValueError = `The ${exampleField} c-quantic-facet requires dependsOn.expectedValue to be a valid string.`; function createTestComponent(options = defaultOptions) { prepareHeadlessState(); @@ -89,6 +96,21 @@ function mockSuccessfulHeadlessInitialization() { }; } +function mockBueno() { + // @ts-ignore + mockHeadlessLoader.getBueno = () => { + // @ts-ignore + global.Bueno = { + isString: jest + .fn() + .mockImplementation( + (value) => Object.prototype.toString.call(value) === '[object String]' + ), + }; + return new Promise((resolve) => resolve()); + }; +} + function cleanup() { // The jsdom instance is shared across test cases in a single file so reset the DOM while (document.body.firstChild) { @@ -101,6 +123,7 @@ function cleanup() { describe('c-quantic-facet', () => { beforeAll(() => { mockSuccessfulHeadlessInitialization(); + mockBueno(); }); afterEach(() => { @@ -214,6 +237,88 @@ describe('c-quantic-facet', () => { }); }); + describe('validation of the dependsOn property', () => { + let consoleError; + beforeAll(() => { + consoleError = jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + describe('when dependsOn.parentFacetId is not provided', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.parentFacetId is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 1, + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.expectedValue is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 'filetype', + expectedValue: 2, + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(expectedValueError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + }); describe('when the component is disconnected', () => { it('should make the condition manager stop watching the facet', async () => { const exampleFacetDependency = { diff --git a/packages/quantic/force-app/main/default/lwc/quanticFacet/quanticFacet.js b/packages/quantic/force-app/main/default/lwc/quanticFacet/quanticFacet.js index 68cf6804f31..69acd2739b9 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticFacet/quanticFacet.js +++ b/packages/quantic/force-app/main/default/lwc/quanticFacet/quanticFacet.js @@ -15,6 +15,7 @@ import { initializeWithHeadless, registerToStore, getHeadlessBundle, + getBueno, } from 'c/quanticHeadlessLoader'; import { I18nUtils, @@ -177,7 +178,6 @@ export default class QuanticFacet extends LightningElement { * @type {DependsOn} */ @api dependsOn; - /** * Whether the facet is collapsed. * @api @@ -315,6 +315,7 @@ export default class QuanticFacet extends LightningElement { element: this.template.host, }); if (this.dependsOn) { + this.validateDependsOnProperty(); this.initFacetConditionManager(engine); } }; @@ -342,6 +343,25 @@ export default class QuanticFacet extends LightningElement { }); this.dispatchEvent(renderFacetEvent); } + validateDependsOnProperty() { + if (this.dependsOn) { + getBueno(this).then(() => { + const {parentFacetId, expectedValue} = this.dependsOn; + if (!Bueno.isString(parentFacetId)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.parentFacetId to be a valid string.` + ); + this.setInitializationError(); + } + if (expectedValue && !Bueno.isString(expectedValue)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.expectedValue to be a valid string.` + ); + this.setInitializationError(); + } + }); + } + } initFacetConditionManager(engine) { this.facetConditionsManager = this.headless.buildFacetConditionsManager( diff --git a/packages/quantic/force-app/main/default/lwc/quanticNumericFacet/__tests__/quanticNumericFacet.test.js b/packages/quantic/force-app/main/default/lwc/quanticNumericFacet/__tests__/quanticNumericFacet.test.js index e9b20d8a8c6..2cd8278e8b2 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticNumericFacet/__tests__/quanticNumericFacet.test.js +++ b/packages/quantic/force-app/main/default/lwc/quanticNumericFacet/__tests__/quanticNumericFacet.test.js @@ -12,17 +12,22 @@ jest.mock('c/quanticUtils', () => ({ NUMERICFACETS: 'numericFacets', }, }, + I18nUtils: { + format: jest.fn(), + }, })); jest.mock('c/quanticHeadlessLoader'); const selectors = { facetContent: '[data-test="facet-content"]', + componentError: 'c-quantic-component-error', }; const exampleFacetId = 'example facet id'; +const exampleField = 'exampleField'; const exampleFilterId = `${exampleFacetId}_input`; const defaultOptions = { - field: 'example field', + field: exampleField, }; const numericFacetControllerMock = { subscribe: jest.fn((callback) => callback()), @@ -31,6 +36,8 @@ const numericFacetControllerMock = { values: [], }, }; +const parentFacetIdError = `The ${exampleField} c-quantic-numeric-facet requires dependsOn.parentFacetId to be a valid string.`; +const expectedValueError = `The ${exampleField} c-quantic-numeric-facet requires dependsOn.expectedValue to be a valid string.`; function createTestComponent(options = defaultOptions) { prepareHeadlessState(); @@ -87,6 +94,21 @@ const exampleEngine = { }; let isInitialized = false; +function mockBueno() { + // @ts-ignore + mockHeadlessLoader.getBueno = () => { + // @ts-ignore + global.Bueno = { + isString: jest + .fn() + .mockImplementation( + (value) => Object.prototype.toString.call(value) === '[object String]' + ), + }; + return new Promise((resolve) => resolve()); + }; +} + function mockSuccessfulHeadlessInitialization() { // @ts-ignore mockHeadlessLoader.initializeWithHeadless = (element, _, initialize) => { @@ -109,6 +131,7 @@ function cleanup() { describe('c-quantic-numeric-facet', () => { beforeAll(() => { mockSuccessfulHeadlessInitialization(); + mockBueno(); }); afterEach(() => { @@ -214,6 +237,89 @@ describe('c-quantic-numeric-facet', () => { }); }); + describe('validation of the dependsOn property', () => { + let consoleError; + beforeAll(() => { + consoleError = jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + describe('when dependsOn.parentFacetId is not provided', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.parentFacetId is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 1, + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.expectedValue is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 'filetype', + expectedValue: 2, + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(expectedValueError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + }); + describe('when the component is disconnected', () => { it('should make the condition manager stop watching the facet', async () => { const exampleFacetDependency = { diff --git a/packages/quantic/force-app/main/default/lwc/quanticNumericFacet/quanticNumericFacet.js b/packages/quantic/force-app/main/default/lwc/quanticNumericFacet/quanticNumericFacet.js index 5e9835e2b49..93db860c475 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticNumericFacet/quanticNumericFacet.js +++ b/packages/quantic/force-app/main/default/lwc/quanticNumericFacet/quanticNumericFacet.js @@ -18,6 +18,7 @@ import { getHeadlessBindings, registerToStore, getHeadlessBundle, + getBueno, } from 'c/quanticHeadlessLoader'; import { I18nUtils, @@ -264,6 +265,7 @@ export default class QuanticNumericFacet extends LightningElement { * @param {SearchEngine} engine */ initialize = (engine) => { + this.validateDependsOnProperty(); this.headless = getHeadlessBundle(this.engineId); this.searchStatus = this.headless.buildSearchStatus(engine); this.unsubscribeSearchStatus = this.searchStatus.subscribe(() => @@ -288,6 +290,26 @@ export default class QuanticNumericFacet extends LightningElement { }); }; + validateDependsOnProperty() { + if (this.dependsOn) { + getBueno(this).then(() => { + const {parentFacetId, expectedValue} = this.dependsOn; + if (!Bueno.isString(parentFacetId)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.parentFacetId to be a valid string.` + ); + this.setInitializationError(); + } + if (expectedValue && !Bueno.isString(expectedValue)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.expectedValue to be a valid string.` + ); + this.setInitializationError(); + } + }); + } + } + /** * @param {import("coveo").SearchEngine} engine */ diff --git a/packages/quantic/force-app/main/default/lwc/quanticTimeframeFacet/__tests__/quanticTimeframeFacet.test.js b/packages/quantic/force-app/main/default/lwc/quanticTimeframeFacet/__tests__/quanticTimeframeFacet.test.js index 7c6a29be374..8c28672e4b7 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticTimeframeFacet/__tests__/quanticTimeframeFacet.test.js +++ b/packages/quantic/force-app/main/default/lwc/quanticTimeframeFacet/__tests__/quanticTimeframeFacet.test.js @@ -12,17 +12,22 @@ jest.mock('c/quanticUtils', () => ({ DATEFACETS: 'dateFacets', }, }, + I18nUtils: { + format: jest.fn(), + }, })); jest.mock('c/quanticHeadlessLoader'); const selectors = { facetContent: '[data-test="facet-content"]', + componentError: 'c-quantic-component-error', }; const exampleFacetId = 'example facet id'; +const exampleField = 'exampleField'; const exampleFilterId = `${exampleFacetId}_input`; const defaultOptions = { - field: 'example field', + field: exampleField, }; const timeframeFacetControllerMock = { subscribe: jest.fn((callback) => callback()), @@ -31,6 +36,8 @@ const timeframeFacetControllerMock = { values: [], }, }; +const parentFacetIdError = `The ${exampleField} c-quantic-timeframe-facet requires dependsOn.parentFacetId to be a valid string.`; +const expectedValueError = `The ${exampleField} c-quantic-timeframe-facet requires dependsOn.expectedValue to be a valid string.`; function createTestComponent(options = defaultOptions) { prepareHeadlessState(); @@ -87,6 +94,21 @@ const exampleEngine = { }; let isInitialized = false; +function mockBueno() { + // @ts-ignore + mockHeadlessLoader.getBueno = () => { + // @ts-ignore + global.Bueno = { + isString: jest + .fn() + .mockImplementation( + (value) => Object.prototype.toString.call(value) === '[object String]' + ), + }; + return new Promise((resolve) => resolve()); + }; +} + function mockSuccessfulHeadlessInitialization() { // @ts-ignore mockHeadlessLoader.initializeWithHeadless = (element, _, initialize) => { @@ -109,6 +131,7 @@ function cleanup() { describe('c-quantic-timeframe-facet', () => { beforeAll(() => { mockSuccessfulHeadlessInitialization(); + mockBueno(); }); afterEach(() => { @@ -211,6 +234,89 @@ describe('c-quantic-timeframe-facet', () => { }); }); + describe('validation of the dependsOn property', () => { + let consoleError; + beforeAll(() => { + consoleError = jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + describe('when dependsOn.parentFacetId is not provided', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.parentFacetId is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 1, + expectedValue: 'txt', + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(parentFacetIdError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + + describe('when dependsOn.expectedValue is not a string', () => { + it('should display the error component', async () => { + const invalidFacetDependency = { + parentFacetId: 'filetype', + expectedValue: 2, + }; + const element = createTestComponent({ + ...defaultOptions, + dependsOn: invalidFacetDependency, + }); + await flushPromises(); + + const componentError = element.shadowRoot.querySelector( + selectors.componentError + ); + const facetContent = element.shadowRoot.querySelector( + selectors.facetContent + ); + + expect(consoleError).toHaveBeenCalledTimes(1); + expect(consoleError).toHaveBeenCalledWith(expectedValueError); + expect(componentError).not.toBeNull(); + expect(facetContent).toBeNull(); + }); + }); + }); + describe('when the component is disconnected', () => { it('should make the condition manager stop watching the facet', async () => { const exampleFacetDependency = { diff --git a/packages/quantic/force-app/main/default/lwc/quanticTimeframeFacet/quanticTimeframeFacet.js b/packages/quantic/force-app/main/default/lwc/quanticTimeframeFacet/quanticTimeframeFacet.js index d2ea07dd6bc..37aa8b30ba2 100644 --- a/packages/quantic/force-app/main/default/lwc/quanticTimeframeFacet/quanticTimeframeFacet.js +++ b/packages/quantic/force-app/main/default/lwc/quanticTimeframeFacet/quanticTimeframeFacet.js @@ -12,6 +12,7 @@ import { initializeWithHeadless, registerComponentForInit, registerToStore, + getBueno, } from 'c/quanticHeadlessLoader'; import { DateUtils, @@ -414,6 +415,7 @@ export default class QuanticTimeframeFacet extends LightningElement { * @param {SearchEngine} engine */ initialize = (engine) => { + this.validateDependsOnProperty(); this.headless = getHeadlessBundle(this.engineId); this.initializeSearchStatusController(engine); this.initializeFacetController(engine); @@ -427,6 +429,26 @@ export default class QuanticTimeframeFacet extends LightningElement { }); }; + validateDependsOnProperty() { + if (this.dependsOn) { + getBueno(this).then(() => { + const {parentFacetId, expectedValue} = this.dependsOn; + if (!Bueno.isString(parentFacetId)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.parentFacetId to be a valid string.` + ); + this.setInitializationError(); + } + if (expectedValue && !Bueno.isString(expectedValue)) { + console.error( + `The ${this.field} ${this.template.host.localName} requires dependsOn.expectedValue to be a valid string.` + ); + this.setInitializationError(); + } + }); + } + } + /** * @param {SearchEngine} engine */