import { type E2EElement, type E2EPage, newE2EPage } from '@stencil/core/testing'; import { ODS_INPUT_TYPE } from '../../src'; describe('ods-input rendering', () => { let el: E2EElement; let page: E2EPage; let buttonClearable: E2EElement; let buttonSearch: E2EElement; let buttonToggleMask: E2EElement; let loadingSpinner: E2EElement; let part: E2EElement; function hexToRGB(hex: string): string { const r = parseInt(hex.slice(1, 3), 16), g = parseInt(hex.slice(3, 5), 16), b = parseInt(hex.slice(5, 7), 16); return `rgb(${r}, ${g}, ${b})`; } async function setup(content: string, customStyle?: string): Promise { page = await newE2EPage(); await page.setContent(content); await page.evaluate(() => document.body.style.setProperty('margin', '0')); if (customStyle) { await page.addStyleTag({ content: customStyle }); } el = await page.find('ods-input'); buttonClearable = await page.find('ods-input >>> ods-button[icon="xmark"]'); buttonSearch = await page.find('ods-input >>> ods-button[icon="magnifying-glass"]'); buttonToggleMask = await page.find('ods-input >>> ods-button[icon="eye-off"]'); loadingSpinner = await page.find('ods-input >>> ods-spinner'); part = await page.find('ods-input >>> [part="input"]'); await page.waitForChanges(); } beforeEach(jest.clearAllMocks); it('should render the web component', async() => { await setup(''); expect(el.shadowRoot).not.toBeNull(); }); describe('props', () => { describe('isDisabled', () => { it('should have the disabled style', async() => { await setup(''); const partStyle = await part.getComputedStyle(); const { border, background, color } = await page.evaluate(() => { const bodyStyles = getComputedStyle(document.body); return { background: bodyStyles.getPropertyValue('--ods-color-background-disabled-default'), border: bodyStyles.getPropertyValue('--ods-color-border-disabled-default'), color: bodyStyles.getPropertyValue('--ods-color-text-disabled-default'), }; }); expect(partStyle.getPropertyValue('borderColor')).toBe(hexToRGB(border)); expect(partStyle.getPropertyValue('backgroundColor')).toBe(hexToRGB(background)); expect(partStyle.getPropertyValue('color')).toBe(hexToRGB(color)); }); it('should have the disabled style with readonly', async() => { await setup(''); const partStyle = await part.getComputedStyle(); const { border, background, color } = await page.evaluate(() => { const bodyStyles = getComputedStyle(document.body); return { background: bodyStyles.getPropertyValue('--ods-color-background-disabled-default'), border: bodyStyles.getPropertyValue('--ods-color-border-disabled-default'), color: bodyStyles.getPropertyValue('--ods-color-text-disabled-default'), }; }); expect(partStyle.getPropertyValue('borderColor')).toBe(hexToRGB(border)); expect(partStyle.getPropertyValue('backgroundColor')).toBe(hexToRGB(background)); expect(partStyle.getPropertyValue('color')).toBe(hexToRGB(color)); }); }); describe('isReadonly', () => { it('should have the readonly style', async() => { await setup(''); const partStyle = await part.getComputedStyle(); const { border, background } = await page.evaluate(() => { const bodyStyles = getComputedStyle(document.body); return { background: bodyStyles.getPropertyValue('--ods-color-background-readonly-default'), border: bodyStyles.getPropertyValue('--ods-color-border-readonly-default'), }; }); expect(partStyle.getPropertyValue('borderColor')).toBe(hexToRGB(border)); expect(partStyle.getPropertyValue('backgroundColor')).toBe(hexToRGB(background)); }); }); }); describe('part', () => { it('should render with custom style applied', async() => { await setup('', 'ods-input::part(input) { width: 100px }'); const partStyle = await part.getComputedStyle(); expect(partStyle.getPropertyValue('width')).toBe('100px'); }); }); describe('form', () => { it('should get form data with button type submit', async() => { await setup(`
`); const submitButton = await page.find('button[type="submit"]'); await submitButton.click(); await page.waitForNetworkIdle(); const url = new URL(page.url()); expect(url.searchParams.get('odsInput')).toBe('On Vous Heberge ?'); expect(url.searchParams.get('natifInput')).toBe(''); }); it('should reset form with button type reset', async() => { await setup(`
`); const resetButton = await page.find('button[type="reset"]'); await resetButton.click(); const submitButton = await page.find('button[type="submit"]'); await submitButton.click(); await page.waitForNetworkIdle(); const url = new URL(page.url()); expect(url.searchParams.get('natifInput')).toBe(''); expect(url.searchParams.get('odsInput')).toBe(''); }); }); describe('slots', () => { describe('list', () => { beforeEach(() => { jest.spyOn(console, 'warn').mockImplementation(() => {}); }); it('should not render with datalist nodes if list prop is not set', async() => { await setup(''); const innerDatalist = await page.find('ods-input >>> datalist'); expect(innerDatalist).toBeNull(); expect(console.warn).toHaveBeenCalledTimes(1); }); it('should warn if list prop is set but without a datalist in the slot', async() => { await setup('

Lorem ipsum

'); const innerDatalist = await page.find('ods-input >>> datalist'); expect(innerDatalist).toBeNull(); expect(console.warn).toHaveBeenCalledTimes(1); }); it('should render with datalist nodes if list prop is set', async() => { await setup(''); const innerDatalist = await page.find('ods-input >>> datalist'); expect(innerDatalist).not.toBeNull(); const innerDatalistOptions = await innerDatalist.findAll('option'); expect(innerDatalistOptions.length).toBe(2); expect(console.warn).toHaveBeenCalledTimes(0); }); describe('onSlotChange', () => { it('should render updated datalist', async() => { await setup(''); el.innerHTML = ''; await page.waitForChanges(); const innerDatalist = await page.find('ods-input >>> datalist'); expect(innerDatalist).not.toBeNull(); const innerDatalistOptions = await innerDatalist.findAll('option'); expect(innerDatalistOptions.length).toBe(1); expect(console.warn).toHaveBeenCalledTimes(0); }); }); }); }); describe('error state', () => { it('should render in error on form submit, before any changes, if invalid', async() => { await setup('
'); await page.evaluate(() => { document.querySelector('form')?.requestSubmit(); }); await page.waitForChanges(); const hasErrorClass = await page.evaluate(() => { return document.querySelector('ods-input')?.shadowRoot?.querySelector('input')?.classList.contains('ods-input__input--error'); }); expect(hasErrorClass).toBe(true); }); it('should toggle the error state on value change', async() => { await setup('
'); await el.type('abcd'); await page.waitForChanges(); const hasErrorClass = await page.evaluate(() => { return document.querySelector('ods-input')?.shadowRoot?.querySelector('input')?.classList.contains('ods-input__input--error'); }); expect(hasErrorClass).toBe(false); await el.callMethod('clear'); await page.click('body', { offset: { x: 200, y: 200 } }); // Blur await page.waitForChanges(); const hasErrorClass2 = await page.evaluate(() => { return document.querySelector('ods-input')?.shadowRoot?.querySelector('input')?.classList.contains('ods-input__input--error'); }); await page.waitForChanges(); expect(hasErrorClass2).toBe(true); }); it('should enforce the error state if has-error is set even on valid input', async() => { await setup('
'); await page.waitForChanges(); const hasErrorClass = await page.evaluate(() => { return document.querySelector('ods-input')?.shadowRoot?.querySelector('input')?.classList.contains('ods-input__input--error'); }); expect(hasErrorClass).toBe(true); await page.evaluate(() => { document.querySelector('form')?.requestSubmit(); }); await page.waitForChanges(); const hasErrorClass2 = await page.evaluate(() => { return document.querySelector('ods-input')?.shadowRoot?.querySelector('input')?.classList.contains('ods-input__input--error'); }); expect(hasErrorClass2).toBe(true); }); }); describe('isMasked', () => { it('should render a toggle mask button', async() => { await setup(''); expect(buttonToggleMask).not.toBeNull(); }); it('should render a disabled toggle mask button when input is disabled', async() => { await setup(''); expect(buttonToggleMask.getAttribute('is-disabled')).toBe(''); }); it('should not render a disabled toggle mask button when input is readonly', async() => { await setup(''); expect(buttonToggleMask.getAttribute('is-disabled')).toBeNull(); }); it('should render toggle mask and clearable button', async() => { await setup(''); expect(buttonClearable).not.toBeNull(); expect(buttonToggleMask).not.toBeNull(); }); it('should render the loading spinner when is-masked and is-loading', async() => { await setup(''); expect(buttonToggleMask).toBeNull(); expect(loadingSpinner).not.toBeNull(); }); }); describe('isClearable', () => { it('should render a clearable button', async() => { await setup(''); expect(buttonClearable).not.toBeNull(); }); it('should render a disabled clearable button when input is disabled', async() => { await setup(''); expect(buttonClearable.getAttribute('is-disabled')).toBe(''); }); it('should render a disabled clearable button when input is readonly', async() => { await setup(''); expect(buttonClearable.getAttribute('is-disabled')).toBe(''); }); it('should render a clearable button when input value wrong type', async() => { await setup(''); expect(buttonClearable).not.toBeNull(); }); it('should render a clearable button when input value 0', async() => { await setup(''); expect(buttonClearable).not.toBeNull(); }); it('should remove clearable button when input type change', async() => { await setup(''); expect(buttonClearable).not.toBeNull(); await el.setProperty('type', ODS_INPUT_TYPE.text); await page.waitForChanges(); buttonClearable = await page.find('ods-input >>> ods-button[icon="xmark"]'); expect(await el.getProperty('value')).toBe(''); expect(buttonClearable).toBeNull(); }); it('should render a clearable button when type', async() => { await setup(''); await el.type('12'); await page.waitForChanges(); buttonClearable = await page.find('ods-input >>> ods-button[icon="xmark"]'); expect(buttonClearable).not.toBeNull(); await page.keyboard.press('Backspace'); await page.keyboard.press('Backspace'); await page.waitForChanges(); buttonClearable = await page.find('ods-input >>> ods-button[icon="xmark"]'); expect(buttonClearable).toBeNull(); // Test E2E launched with chrome. Chrome don't allow to type on input type number await page.keyboard.press('e'); await page.waitForChanges(); buttonClearable = await page.find('ods-input >>> ods-button[icon="xmark"]'); expect(buttonClearable).not.toBeNull(); }); }); describe('type search', () => { it('should render a search button', async() => { await setup(''); expect(await el.getProperty('type')).toBe('search'); expect(buttonSearch).not.toBeNull(); }); it('should render a disabled search button when input is disabled', async() => { await setup(''); expect(buttonSearch.getAttribute('is-disabled')).toBe(''); }); it('should render a disabled search button when input is readonly', async() => { await setup(''); expect(buttonSearch.getAttribute('is-disabled')).toBe(''); }); it('should render search button and clearable button', async() => { await setup(''); expect(buttonClearable).not.toBeNull(); expect(buttonSearch).not.toBeNull(); }); it('should render the loading spinner when input of type search is-loading', async() => { await setup(''); expect(buttonSearch).toBeNull(); expect(loadingSpinner).not.toBeNull(); }); }); });