Skip to content

Commit

Permalink
fix: dynamic crf e2e coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
mirovladimitrovski committed Oct 25, 2023
1 parent 3574a16 commit 0b35179
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 33 deletions.
4 changes: 2 additions & 2 deletions src/components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ const Checkbox: React.FC<Props> = ({ label, name, onChange, header, checked, val
const id = useOpaqueId('check-box', name);

return (
<div className={classNames(styles.checkbox, { [styles.error]: error })}>
<div className={classNames(styles.checkbox, { [styles.error]: error })} {...rest}>
{header ? (
<div className={styles.header}>
{header}
{!required ? <span>{t('optional')}</span> : null}
</div>
) : null}
<div className={styles.row}>
<input name={name} type="checkbox" id={id} value={value} onChange={onChange} checked={checked} aria-required={required} disabled={disabled} {...rest} />
<input name={name} type="checkbox" id={id} value={value} onChange={onChange} checked={checked} aria-required={required} disabled={disabled} />
<label htmlFor={id}>
{required ? '* ' : ''}
{label}
Expand Down
12 changes: 5 additions & 7 deletions src/components/CustomRegisterField/CustomRegisterField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,19 @@ export const CustomRegisterField: FC<Props> = ({ type, value = '', ...props }) =
}
}, [t, type, props.options, i18n]);

const commonProps = { 'data-testid': testId(`crf-${type}`), ...props };

switch (type) {
case 'input':
return <TextField {...commonProps} value={value as string} />;
return <TextField {...props} value={value as string} testId={testId(`crf-${type}`)} />;
case 'radio':
return <Radio {...commonProps} values={optionsList} value={value as string} header={props.label} />;
return <Radio {...props} values={optionsList} value={value as string} header={props.label} data-testid={testId(`crf-${type}`)} />;
case 'select':
case 'country':
case 'us_state':
return <Dropdown {...commonProps} options={optionsList} value={value as string} defaultLabel={props.placeholder} fullWidth />;
return <Dropdown {...props} options={optionsList} value={value as string} defaultLabel={props.placeholder} fullWidth testId={testId(`crf-${type}`)} />;
case 'datepicker':
return <DateField {...commonProps} value={value as string} />;
return <DateField {...props} value={value as string} testId={testId(`crf-${type}`)} />;
default:
return <Checkbox {...commonProps} checked={isTruthyCustomParamValue(value)} />;
return <Checkbox {...props} checked={isTruthyCustomParamValue(value)} data-testid={testId(`crf-${type}`)} />;
}
};

Expand Down
6 changes: 4 additions & 2 deletions src/components/DateField/DateField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Props = {
helperText?: React.ReactNode;
error?: boolean;
required?: boolean;
testId?: string;
};

const parseDateString = (dateString: string) => {
Expand All @@ -37,6 +38,7 @@ const DateField: React.FC<Props> = ({
name,
required,
onFocus,
testId,
...rest
}: Props) => {
const { t } = useTranslation('common');
Expand Down Expand Up @@ -139,14 +141,14 @@ const DateField: React.FC<Props> = ({
};

return (
<div className={DateFieldClassName} {...rest}>
<div className={DateFieldClassName} {...rest} data-testid={testId} id={id}>
<label htmlFor={id} className={styles.label}>
{label}
{!required ? <span>{t('optional')}</span> : null}
</label>
<div className={styles.container}>
{/* don't be tempted to make it type="hidden", onChange will practically be ignored that way */}
<input ref={hiddenInputRef} id={id} className={styles.hiddenInput} name={name} onChange={onChange} />
<input ref={hiddenInputRef} id={`${id}-hidden`} className={styles.hiddenInput} name={name} onChange={onChange} />
<input
className={styles.input}
name="date"
Expand Down
4 changes: 3 additions & 1 deletion src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Props = {
helperText?: string;
required?: boolean;
onChange: React.ChangeEventHandler;
testId?: string;
};

const Dropdown: React.FC<Props & React.AriaAttributes> = ({
Expand All @@ -37,13 +38,14 @@ const Dropdown: React.FC<Props & React.AriaAttributes> = ({
helperText,
required = false,
size = 'medium',
testId,
...rest
}: Props & React.AriaAttributes) => {
const { t } = useTranslation('common');
const id = useOpaqueId();

return (
<div className={classNames(styles.container, { [styles.fullWidth]: fullWidth, [styles.error]: error }, styles[size], className)}>
<div className={classNames(styles.container, { [styles.fullWidth]: fullWidth, [styles.error]: error }, styles[size], className)} data-testid={testId}>
{(label || !required) && (
<label htmlFor={id} className={styles.label}>
{label}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ const Radio: React.FC<Props> = ({ name, onChange, header, value, values, helperT

return (
<div className={error ? styles.error : undefined} {...rest}>
{header ? (
<div className={styles.header}>
{header || !required ? (
<div className={styles.header} data-testid="radio-header">
{header}
{!required ? <span>{t('optional')}</span> : null}
</div>
Expand Down
2 changes: 1 addition & 1 deletion test-e2e/codecept.desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ exports.config = {
helpers: {
Playwright: {
url: 'http://localhost:8080',
show: !!process.env.SHOW,
show: true, //!!process.env.SHOW,
channel: 'chrome',
locale: 'en-US',
keepCookies: false,
Expand Down
7 changes: 7 additions & 0 deletions test-e2e/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ export default {
customRegFields: {
topContainerSelector: 'div[data-testid="custom-reg-fields"]',
termsAndConditionsField: 'input[type="checkbox"][name="terms"]',
crfTextInput: '[data-testid="crf-input"]',
crfCheckbox: '[data-testid="crf-checkbox"]',
crfCountrySelect: '[data-testid="crf-country"]',
crfUsStateSelect: '[data-testid="crf-us_state"]',
crfDropdownSelect: '[data-testid="crf-select"]',
crfRadioBox: '[data-testid="crf-radio"]',
crfDateField: '[data-testid="crf-datepicker"]',
},
offers: {
monthlyOffer: {
Expand Down
9 changes: 9 additions & 0 deletions test-e2e/utils/randomizers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const random = (min = 0, max = 0) => min + Math.round(Math.random() * (max - min));

const randomDay = () => random(1, 28);

const randomMonth = () => random(1, 12);

const randomYear = () => random(2050, 2100);

export const randomDate = () => [randomDay(), randomMonth(), randomYear()];
111 changes: 93 additions & 18 deletions test-e2e/utils/steps_file.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as assert from 'assert';

import { randomDate } from './randomizers';

import constants, { makeShelfXpath, normalTimeout, ShelfId } from '#utils/constants';
import passwordUtils, { LoginContext } from '#utils/password_utils';
import { TestConfig } from '#test/types';
Expand Down Expand Up @@ -97,32 +99,105 @@ const stepsObj = {
this.checkOption(constants.customRegFields.termsAndConditionsField);
}

// Dropdown select field ("select")
this.selectOption('select[name="Pol"]', 'Female');
await this.checkRequiredCheckboxes();
await this.fillRequiredTextInputs();
await this.checkRequiredRadioBoxes();
await this.selectFromRequiredDropdownLists();
await this.fillRequiredDateFields();
},
checkRequiredCheckboxes: async function (this: CodeceptJS.I) {
const container = constants.customRegFields.topContainerSelector;

const requiredCheckboxNames: string[] = await this.executeScript(
([container, crfFieldSelector]: [string, string]) =>
Array.from(document.querySelectorAll(`${container} ${crfFieldSelector}`))
.filter((element) => element.querySelector('label')?.innerText.startsWith('*'))
.map((element) => (element.querySelector('input[type="checkbox"]') as HTMLInputElement)?.name),
[container, constants.customRegFields.crfCheckbox],
);

await within(container, () => {
requiredCheckboxNames.forEach((name) => {
this.checkOption(name);
});
});
},
fillRequiredTextInputs: async function (this: CodeceptJS.I) {
const container = constants.customRegFields.topContainerSelector;

// US State select field ("us_state")
this.selectOption('select[name="us_state"]', 'Idaho');
const requiredTextInputNames: string[] = await this.executeScript(
([container, crfFieldSelector]: [string, string]) =>
Array.from(document.querySelectorAll(`${container} ${crfFieldSelector}`))
.filter((element) => !element.querySelector('label')?.innerText.includes('(Optional)'))
.map((element) => (element.querySelector('input[type="text"]') as HTMLInputElement)?.name),
[container, constants.customRegFields.crfTextInput],
);

// Text input field ("input")
this.fillField('input[name="text123"]', 'Random test text');
await within(container, () => {
requiredTextInputNames.forEach((inputField) => {
this.fillField(inputField, 'Random text');
});
});
},
checkRequiredRadioBoxes: async function (this: CodeceptJS.I) {
const container = constants.customRegFields.topContainerSelector;

// Radio field ("radio")
within('div[label="rb test"]', () => {
this.checkOption('input[type="radio"][value="rb2"]');
const requiredRadioValues: string[] = await this.executeScript(
([container, crfFieldSelector]: [string, string]) =>
Array.from(document.querySelectorAll(`${container} ${crfFieldSelector}`))
.filter((element) => !(element.querySelector('[data-testid="radio-header"]') as HTMLElement)?.innerText.includes('(Optional)'))
.map((element) => (element.querySelector('input[type="radio"]') as HTMLInputElement)?.value),
[container, constants.customRegFields.crfRadioBox],
);

await within(container, () => {
requiredRadioValues.forEach((radioValue) => {
this.checkOption(`[value="${radioValue}"]`);
});
});
},
selectFromRequiredDropdownLists: async function (this: CodeceptJS.I) {
const container = constants.customRegFields.topContainerSelector;

const requiredDropdownNames: string[] = await this.executeScript((container: string) => {
const querySelector = ['crf-select', 'crf-country', 'crf-us_state'].map((testId) => `${container} [data-testid="${testId}"]`).join(', ');

return Array.from(document.querySelectorAll(querySelector))
.filter((element) => !element.querySelector('label')?.innerText.includes('(Optional)'))
.map((element) => element.querySelector('select')?.name);
}, container);

// Country select field ("country")
this.selectOption('select[name="country"]', 'Australia');
await within(container, () => {
requiredDropdownNames.forEach(async (dropdownName) => {
const firstOption: string = await this.executeScript(
(dropdownName: string) => (document.querySelector(`select[name="${dropdownName}"] option:not(:disabled)`) as HTMLOptionElement)?.value,
dropdownName,
);

// Date picker ("datepicker")
within('[data-testid="crf-datepicker"]', () => {
this.fillField('[name="date"]', '25');
this.fillField('[name="month"]', '06');
this.fillField('[name="year"]', '2050');
this.selectOption(`select[name="${dropdownName}"]`, firstOption);
});
});
},
fillRequiredDateFields: async function (this: CodeceptJS.I) {
const container = constants.customRegFields.topContainerSelector;

this.checkOption('Yes, I want to receive Blender updates by email');
this.checkOption('Check me');
const requiredDatepickerIds: string[] = await this.executeScript(
([container, crfFieldSelector]: [string, string]) =>
Array.from(document.querySelectorAll(`${container} ${crfFieldSelector}`))
.filter((element) => !element.querySelector('label')?.innerText.includes('(Optional)'))
.map((element) => element.id),
[container, constants.customRegFields.crfDateField],
);

requiredDatepickerIds.forEach(async (datepickerId) => {
await within(`#${datepickerId}`, () => {
const [day, month, year] = randomDate();

this.fillField('date', day);
this.fillField('month', month);
this.fillField('year', year);
});
});
},
submitForm: function (this: CodeceptJS.I, loaderTimeout: number | false = normalTimeout) {
this.click('button[type="submit"]');
Expand Down

0 comments on commit 0b35179

Please sign in to comment.