diff --git a/packages/ods/src/components/password/src/components/ods-password/ods-password.tsx b/packages/ods/src/components/password/src/components/ods-password/ods-password.tsx index 715dcd1ff2..fcbe7c9cdb 100644 --- a/packages/ods/src/components/password/src/components/ods-password/ods-password.tsx +++ b/packages/ods/src/components/password/src/components/ods-password/ods-password.tsx @@ -1,11 +1,9 @@ import { AttachInternals, Component, Event, type EventEmitter, type FunctionalComponent, Host, Listen, Method, Prop, State, h } from '@stencil/core'; import { submitFormOnEnter } from '../../../../../utils/dom'; import { type OdsInput, type OdsInputChangeEvent } from '../../../../input/src'; -import { updateInternals } from '../../controller/ods-password'; +import { VALUE_DEFAULT_VALUE, getInitialValue, updateInternals } from '../../controller/ods-password'; import { type OdsPasswordChangeEventDetail } from '../../interfaces/events'; -const VALUE_DEFAULT_VALUE = null; - @Component({ formAssociated: true, shadow: { @@ -104,9 +102,7 @@ export class OdsPassword { } componentWillLoad(): void { - if (!this.value && (this.value !== VALUE_DEFAULT_VALUE || this.defaultValue)) { - this.value = this.defaultValue ?? null; - } + this.value = getInitialValue(this.value, this.defaultValue); } async componentDidLoad(): Promise { @@ -135,7 +131,8 @@ export class OdsPassword { render(): FunctionalComponent { return ( - { internals.setFormValue(value?.toString() ?? ''); @@ -10,5 +20,7 @@ async function updateInternals(internals: ElementInternals, value: string | null } export { + getInitialValue, updateInternals, + VALUE_DEFAULT_VALUE, }; diff --git a/packages/ods/src/components/password/tests/behaviour/ods-password.e2e.ts b/packages/ods/src/components/password/tests/behaviour/ods-password.e2e.ts index 2ad2512642..3673c84bde 100644 --- a/packages/ods/src/components/password/tests/behaviour/ods-password.e2e.ts +++ b/packages/ods/src/components/password/tests/behaviour/ods-password.e2e.ts @@ -1,4 +1,5 @@ import { type E2EElement, type E2EPage, newE2EPage } from '@stencil/core/testing'; +import { type OdsPasswordChangeEventDetail } from '../../src'; describe('ods-password behaviour', () => { let el: E2EElement; @@ -15,6 +16,105 @@ describe('ods-password behaviour', () => { beforeEach(jest.clearAllMocks); + describe('initialization', () => { + let odsChangeEventCount = 0; + let odsChangeEventDetail = {}; + + async function setupWithSpy(content: string): Promise { + odsChangeEventCount = 0; + odsChangeEventDetail = {}; + page = await newE2EPage(); + + // page.spyOnEvent doesn't seems to work to observe event emitted on first render, before any action happens + // so we spy manually + await page.exposeFunction('onOdsChangeEvent', (detail: OdsPasswordChangeEventDetail) => { + odsChangeEventCount++; + odsChangeEventDetail = detail; + }); + + await page.evaluateOnNewDocument(() => { + window.addEventListener('odsChange', (event: Event) => { + // @ts-ignore function is exposed manually + window.onOdsChangeEvent((event as CustomEvent).detail); + }); + }); + + await page.setContent(content); + } + + describe('with no value attribute defined', () => { + it('should trigger a uniq odsChange event', async() => { + await setupWithSpy(''); + + expect(odsChangeEventCount).toBe(1); + expect(odsChangeEventDetail).toEqual({ + validity: {}, + value: null, + }); + }); + }); + + describe('with empty string value', () => { + it('should trigger a uniq odsChange event', async() => { + await setupWithSpy(''); + + expect(odsChangeEventCount).toBe(1); + expect(odsChangeEventDetail).toEqual({ + validity: {}, + value: '', + }); + }); + }); + + describe('with no value but empty default-value', () => { + it('should trigger a uniq odsChange event', async() => { + await setupWithSpy(''); + + expect(odsChangeEventCount).toBe(1); + expect(odsChangeEventDetail).toEqual({ + validity: {}, + value: '', + }); + }); + }); + + describe('with no value but default-value defined', () => { + it('should trigger a uniq odsChange event', async() => { + await setupWithSpy(''); + + expect(odsChangeEventCount).toBe(1); + expect(odsChangeEventDetail).toEqual({ + validity: {}, + value: 'default', + }); + }); + }); + + describe('with defined value', () => { + it('should trigger a uniq odsChange event', async() => { + await setupWithSpy(''); + + expect(odsChangeEventCount).toBe(1); + expect(odsChangeEventDetail).toEqual({ + validity: {}, + value: 'value', + }); + }); + }); + + describe('with defined value and default value', () => { + it('should trigger a uniq odsChange event', async() => { + await setupWithSpy(''); + + expect(odsChangeEventCount).toBe(1); + expect(odsChangeEventDetail).toEqual({ + validity: {}, + value: 'value', + }); + }); + }); + }); + describe('methods', () => { describe('clear', () => { it('should receive odsClear event', async() => { diff --git a/packages/ods/src/components/password/tests/controller/ods-password.spec.ts b/packages/ods/src/components/password/tests/controller/ods-password.spec.ts new file mode 100644 index 0000000000..0337e2db65 --- /dev/null +++ b/packages/ods/src/components/password/tests/controller/ods-password.spec.ts @@ -0,0 +1,68 @@ +jest.mock('../../../../utils/dom'); + +import { setInternalsValidityFromOdsComponent } from '../../../../utils/dom'; +import { type OdsInput } from '../../../input/src'; +import { getInitialValue, updateInternals } from '../../src/controller/ods-password'; + +describe('ods-password controller', () => { + beforeEach(jest.clearAllMocks); + + describe('getInitialValue', () => { + it('should return null if value is null and no default value', () => { + expect(getInitialValue(null)).toBe(null); + }); + + it('should return string if value is set regarding of default value', () => { + expect(getInitialValue('')).toBe(''); + expect(getInitialValue('value')).toBe('value'); + expect(getInitialValue('value', 'default')).toBe('value'); + // @ts-ignore for test purpose + expect(getInitialValue('value', null)).toBe('value'); + }); + + it('should return default value if value is null', () => { + expect(getInitialValue(null, '')).toBe(''); + expect(getInitialValue(null, 'default')).toBe('default'); + }); + }); + + describe('updateInternals', () => { + const dummyInput = { dummy: 'input' }; + const dummyInternal = { + setFormValue: jest.fn(), + } as unknown as ElementInternals; + + it('should set internal value with empty string', async() => { + // @ts-ignore for test purpose + await updateInternals(dummyInternal); + expect(dummyInternal.setFormValue).toHaveBeenCalledWith(''); + + // @ts-ignore for test purpose + await updateInternals(dummyInternal, undefined, {} as HTMLElement & OdsInput); + expect(dummyInternal.setFormValue).toHaveBeenCalledWith(''); + + await updateInternals(dummyInternal, null, {} as HTMLElement & OdsInput); + expect(dummyInternal.setFormValue).toHaveBeenCalledWith(''); + }); + + it('should set internal value with string value', async() => { + const dummyValue = 'dummy value'; + + await updateInternals(dummyInternal, dummyValue, {} as HTMLElement & OdsInput); + + expect(dummyInternal.setFormValue).toHaveBeenCalledWith(dummyValue); + }); + + it('should not set internal validity if no input element is defined', async() => { + await updateInternals(dummyInternal, 'dummyValue'); + + expect(setInternalsValidityFromOdsComponent).not.toHaveBeenCalled(); + }); + + it('should set internal validity if input element is defined', async() => { + await updateInternals(dummyInternal, 'dummyValue', dummyInput as unknown as HTMLElement & OdsInput); + + expect(setInternalsValidityFromOdsComponent).toHaveBeenCalledWith(dummyInput, dummyInternal); + }); + }); +});