wix-style-react
Version:
303 lines (265 loc) • 9.21 kB
JavaScript
import {
baseUniDriverFactory,
ReactBase,
findByHook,
getDataAttributeValue,
} from '../../test/utils/unidriver';
import {
DATA_OPTION,
DATA_HOOKS,
DATA_DIRECTION,
DATA_SHOWN,
DROPDOWN_LAYOUT_DIRECTIONS,
OPTION_DATA_HOOKS,
DATA_SELECTED_OPTION_ID,
} from './DataAttr';
export const dropdownLayoutDriverFactory = base => {
const reactBase = ReactBase(base);
const contentContainer = () => findByHook(base, DATA_HOOKS.CONTENT_CONTAINER);
const infiniteScrollContainer = () =>
findByHook(base, DATA_HOOKS.INFINITE_SCROLL_CONTAINER);
const optionsElement = () =>
findByHook(base, DATA_HOOKS.DROPDOWN_LAYOUT_OPTIONS);
async function getOptionsContainerDataHook() {
return (await infiniteScrollContainer().exists())
? DATA_HOOKS.INFINITE_SCROLL_CONTAINER
: DATA_HOOKS.DROPDOWN_LAYOUT_OPTIONS;
}
const optionElementAt = async position =>
await base.$(
`[data-hook=${await getOptionsContainerDataHook()}] > *:nth-child(${
position + 1
})`,
);
const optionElementByDataHook = async dataHook =>
await base.$(
`[data-hook=${await getOptionsContainerDataHook()}] [data-hook="${dataHook}"]`,
);
const options = async () =>
base
.$$(`[data-hook=${await getOptionsContainerDataHook()}] > *`)
.map(i => i);
const optionsLength = async () => (await options()).length;
const doIfOptionExists = (position, onSuccess) => {
if (optionsLength() <= position) {
throw new Error(
`index out of bounds, try to get option ${position} while only ${optionsLength()} options exists`,
);
}
return onSuccess();
};
const getOptionDriver = position =>
doIfOptionExists(position, async () =>
createOptionDriver(await optionElementAt(position)),
);
return {
...baseUniDriverFactory(base),
/** @deprecated should be private */
classes: () => optionsElement()._prop('className'),
/** Clicks on an option at a specific index
* @param {number} option index
* @return {Promise<void>}
*/
clickAtOption: async index => {
const optionDriver = await getOptionDriver(index);
return optionDriver.click();
},
/** Clicks on an option with a specific dataHook
* @param {string} dataHook
* @return {Promise<void>}
*/
clickAtOptionByDataHook: async dataHook =>
(await optionElementByDataHook(dataHook)).click(),
/** Clicks on an option with a specific value
* @param {string} value
* @return {Promise<void>}
*/
clickAtOptionWithValue: async value => {
for (const _option of await options()) {
const optionDriver = await createOptionDriver(_option);
if ((await optionDriver.content()) === value) {
return optionDriver.click();
}
}
},
/** @deprecated deprecated prop */
hasTopArrow: async () =>
await (await findByHook(base, DATA_HOOKS.TOP_ARROW)).exists(),
/** @deprecated deprecated prop */
isDown: async () =>
(await (await contentContainer()).attr(DATA_DIRECTION)) ===
DROPDOWN_LAYOUT_DIRECTIONS.DOWN,
/** @deprecated deprecated prop */
isUp: async () =>
(await (await contentContainer()).attr(DATA_DIRECTION)) ===
DROPDOWN_LAYOUT_DIRECTIONS.UP,
isLinkOption: async position =>
doIfOptionExists(position, async () => {
const optionDriver = await getOptionDriver(position);
return optionDriver.isLink();
}),
isOptionADivider: position =>
doIfOptionExists(position, async () => {
const optionDriver = await getOptionDriver(position);
return optionDriver.isDivider();
}),
isOptionExists: async optionText => {
for (const _option of await options()) {
if ((await _option.text()) === optionText) {
return true;
}
}
return false;
},
isOptionHovered: async index =>
doIfOptionExists(index, async () => {
const optionDriver = await getOptionDriver(index);
return optionDriver.isHovered();
}),
isOptionSelected: async index =>
doIfOptionExists(index, async () => {
const optionDriver = await getOptionDriver(index);
return optionDriver.isSelected();
}),
/** @deprecated */
isOptionHeightSmall: position =>
doIfOptionExists(
position,
async () =>
(await (await optionElementAt(position)).attr(DATA_OPTION.SIZE)) ===
'small',
),
/** @deprecated */
isOptionHeightBig: position =>
doIfOptionExists(
position,
async () =>
(await (await optionElementAt(position)).attr(DATA_OPTION.SIZE)) ===
'big',
),
isShown: async () => !!(await (await contentContainer()).attr(DATA_SHOWN)),
mouseEnter: () => base.hover(),
mouseEnterAtOption: position =>
doIfOptionExists(position, async () => {
const optionDriver = await getOptionDriver(position);
return optionDriver.mouseEnter();
}),
mouseLeave: async () => {
switch (base.type) {
case 'react':
reactBase.mouseLeave();
return;
case 'puppeteer':
const { element } = await baseUniDriverFactory(base).element();
page.evaluate(element => {
element.dispatchEvent(
new MouseEvent('mouseout', {
bubbles: true,
view: window,
cancelable: true,
}),
);
}, element);
return;
default:
return;
}
},
/** @deprecated deprecated prop */
mouseClickOutside: () => ReactBase.clickBody(),
mouseLeaveAtOption: position =>
doIfOptionExists(position, async () => {
const optionDriver = await getOptionDriver(position);
return optionDriver.mouseLeave();
}),
/** @deprecated Use optionDriver*/
optionAt: () => {
return optionElementAt.getNative(); // eslint-disable-line no-restricted-properties
},
// This should be a private method since the hook include internal parts ('dropdown-divider-{id}, dropdown-item-{id})') */
/** @deprecated */
optionByHook: async hook => {
const option = optionsElement().$(`[data-hook=${hook}]`);
if (!(await option.exists())) {
throw new Error(`an option with data-hook ${hook} was not found`);
}
return createOptionDriver(option);
},
/**
* Get Option by id
* @returns {Promise<any>}
*/
optionById(optionId) {
return this.optionByHook(`dropdown-item-${optionId}`);
},
optionContentAt: position =>
doIfOptionExists(position, async () => {
const optionDriver = await getOptionDriver(position);
return optionDriver.content();
}),
optionDriver: createOptionDriver,
/** Get Options drivers */
options: async () => {
const drivers = [];
for (let position = 0; position < (await optionsLength()); position++) {
drivers.push(await getOptionDriver(position));
}
return drivers;
},
optionsContent: async () => {
const contentArray = [];
for (const option of await options()) {
const optionDriver = await createOptionDriver(option);
contentArray.push(await optionDriver.content());
}
return contentArray;
},
markedOption: async () => {
const allOptions = await options();
const optionsWithHovered = await Promise.all(
allOptions.map(async option => ({
option,
hovered: !!(await option.attr(DATA_OPTION.HOVERED)),
})),
);
const hoveredOptions = optionsWithHovered
.filter(option => option.hovered)
.map(option => option.option);
return (
(hoveredOptions.length &&
createOptionDriver(hoveredOptions[0]).content()) ||
null
);
},
getSelectedOptionId: async () =>
await getDataAttributeValue(
await contentContainer(),
DATA_SELECTED_OPTION_ID,
),
optionsLength,
/** @deprecated should be private */
optionsScrollTop: () => optionsElement()._prop('scrollTop'),
pressDownKey: () => base.pressKey('ArrowDown'),
pressUpKey: () => base.pressKey('ArrowUp'),
pressEnterKey: () => base.pressKey('Enter'),
pressSpaceKey: () => base.pressKey(' '),
pressTabKey: () => base.pressKey('Tab'),
pressEscKey: () => base.pressKey('Escape'),
tabIndex: () => base._prop('tabIndex'),
};
};
const createOptionDriver = option => ({
element: () => option,
mouseEnter: () => option.hover(),
mouseLeave: () => ReactBase(option).mouseLeave(),
isHovered: async () => !!(await option.attr(DATA_OPTION.HOVERED)),
isSelected: async () => !!(await option.attr(DATA_OPTION.SELECTED)),
content: () => option.text(),
click: () => option.click(),
isDivider: async () => {
const divider = await findByHook(option, OPTION_DATA_HOOKS.DIVIDER);
return divider.exists();
},
isDisabled: async () => !!(await option.attr(DATA_OPTION.DISABLED)),
isLink: async () => (await option._prop('tagName')).toLowerCase() === 'a',
});