test(item): form data for items with controls (#25624)

This commit is contained in:
Sean Perkins
2022-07-14 11:53:42 -04:00
committed by GitHub
parent 64ae3d2b97
commit fe757c35ee
98 changed files with 186 additions and 253 deletions

View File

@ -19,56 +19,77 @@
<ion-header>
<ion-toolbar>
<ion-title>Item inputs</ion-title>
<ion-buttons slot="end">
<ion-button id="popover-trigger">Options</ion-button>
</ion-buttons>
<ion-popover id="optionsPopover" trigger="popover-trigger">
<ion-list lines="none">
<ion-item id="btnDisabled" button="true" onclick="toggleDisabled()">
<ion-label>Toggle Disabled State</ion-label>
</ion-item>
<ion-item id="btnSomeValue" button="true" onclick="setSomeValue()">
<ion-label>Set initial values</ion-label>
</ion-item>
<ion-item id="btnEmptyValue" button="true" onclick="setEmptyValue()">
<ion-label>Set empty values</ion-label>
</ion-item>
<ion-item id="btnNullValue" button="true" onclick="setNullValue()">
<ion-label>Set "null" values</ion-label>
</ion-item>
<ion-item id="btnUndefinedValue" button="true" onclick="setUndefinedValue()">
<ion-label>Set "undefined" values</ion-label>
</ion-item>
<ion-item id="btnLabelsDefault" button="true" onclick="setLabelDefault()">
<ion-label>Labels: Default</ion-label>
</ion-item>
<ion-item id="btnLabelsFloating" button="true" onclick="setLabelFloating()">
<ion-label>Labels: Floating</ion-label>
</ion-item>
<ion-item id="btnLabelsStacked" button="true" onclick="setLabelStacked()">
<ion-label>Labels: Stacked</ion-label>
</ion-item>
<ion-item id="btnLabelsFixed" button="true" onclick="setLabelFixed()">
<ion-label>Labels: Fixed</ion-label>
</ion-item>
</ion-list>
</ion-popover>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding-vertical">
<form onsubmit="return onSubmit(event)">
<ion-list class="basic">
<ion-list>
<ion-item>
<ion-label>Simple item</ion-label>
</ion-item>
<ion-item id="item" button onclick="testClick(event)">
<ion-label>Item Button</ion-label>
<ion-label>Input</ion-label>
<ion-input name="input" id="input" placeholder="Input"></ion-input>
</ion-item>
<ion-item>
<ion-label>DateTime</ion-label>
<ion-datetime name="date" id="datetime" value="2022-04-01T10:00"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>Select</ion-label>
<ion-select name="select" id="select" value="n64">
<ion-select-option value="">No Game Console</ion-select-option>
<ion-select-option value="nes">NES</ion-select-option>
<ion-select-option value="n64">Nintendo64</ion-select-option>
<ion-select-option value="ps">PlayStation</ion-select-option>
<ion-select-option value="genesis">Sega Genesis</ion-select-option>
<ion-select-option value="saturn">Sega Saturn</ion-select-option>
<ion-select-option value="snes">SNES</ion-select-option>
</ion-select>
<ion-label>Textarea</ion-label>
<ion-textarea name="textarea" id="textarea" placeholder="Textarea"></ion-textarea>
</ion-item>
<ion-item>
<ion-label>Toggle</ion-label>
<ion-toggle name="toggle" id="toggle" name="Actually" slot="end"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Input (text)</ion-label>
<ion-input name="input" id="text"></ion-input>
</ion-item>
<ion-item>
<ion-label>Input (placeholder)</ion-label>
<ion-input name="input2" id="placeholder" placeholder="Placeholder"></ion-input>
<ion-toggle name="toggle" id="toggle" slot="end"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Checkbox</ion-label>
<ion-checkbox name="checkbox" id="checkbox" slot="start"></ion-checkbox>
<ion-checkbox name="checkbox" id="checkbox" slot="end"></ion-checkbox>
</ion-item>
<ion-item>
<ion-label>Select</ion-label>
<ion-select name="select" id="select" placeholder="Select">
<ion-select-option value="1">1</ion-select-option>
<ion-select-option value="2">2</ion-select-option>
<ion-select-option value="3">3</ion-select-option>
</ion-select>
</ion-item>
<ion-item>
<ion-datetime name="datetime" id="datetime" value="2022-04-01T10:00"></ion-datetime>
</ion-item>
<ion-item>
@ -77,154 +98,52 @@
</ion-item>
</ion-list>
<ion-button id="submit" type="submit">Submit</ion-button>
<p id="form-result">Form not submitted</p>
<ion-button class="ion-padding-horizontal ion-margin-top" id="submit" type="submit" expand="block"
>Submit
</ion-button>
<p id="form-result"></p>
</form>
<ion-list>
<ion-list-header>
<ion-label> Controls </ion-label>
</ion-list-header>
<ion-item-divider>Value Controls</ion-item-divider>
<ion-item button onClick="toggleDisabled()" id="btnDisabled"> Toggle Disable </ion-item>
<ion-item button onClick="setSomeValue()" id="btnSomeValue"> Set some value </ion-item>
<ion-item button onClick="setEmptyValue()" id="btnEmptyValue"> Set empty value </ion-item>
<ion-item button onClick="setNullValue()" id="btnNullValue"> Set "null" value </ion-item>
<ion-item button onClick="setUndefinedValue()" id="btnUndefinedValue"> Set "undefined" value </ion-item>
<ion-item-divider>Label Controls</ion-item-divider>
<ion-item button onClick="setLabelDefault()" id="btnLabelDefault"> Default </ion-item>
<ion-item button onClick="setLabelFloating()" id="btnLabelFloating"> Floating </ion-item>
<ion-item button onClick="setLabelStacked()" id="btnLabelStacked"> Stacked </ion-item>
<ion-item button onClick="setLabelFixed()" id="btnLabelFixed"> Fixed </ion-item>
</ion-list>
<ion-list-header>
<ion-label> Multiple inputs/clickables </ion-label>
</ion-list-header>
<ion-list class="multiple">
<ion-item>
<ion-label>Multiple inputs</ion-label>
<ion-input placeholder="Input 1"></ion-input>
<ion-input placeholder="Input 2"></ion-input>
<ion-input placeholder="Input 3"></ion-input>
</ion-item>
<ion-item>
<ion-label>Multiple textareas</ion-label>
<ion-textarea placeholder="Textarea 1"></ion-textarea>
<ion-textarea placeholder="Textarea 2"></ion-textarea>
<ion-textarea placeholder="Textarea 3"></ion-textarea>
</ion-item>
<ion-item>
<ion-label>Multiple input/textareas</ion-label>
<ion-textarea placeholder="Textarea 1"></ion-textarea>
<ion-input placeholder="Input 2"></ion-input>
<ion-textarea placeholder="Textarea 3"></ion-textarea>
</ion-item>
<ion-item>
<ion-checkbox slot="start" id="checkbox-start"></ion-checkbox>
<ion-label>Multiple inputs w/ cover</ion-label>
<ion-datetime placeholder="startTime" id="datetime-end" value="2022-04-01T10:00"> </ion-datetime>
<ion-checkbox slot="end" id="checkbox-end"></ion-checkbox>
</ion-item>
<ion-item>
<ion-select slot="start" placeholder="month" id="select-start">
<ion-select-option value="1">January</ion-select-option>
<ion-select-option value="2">February</ion-select-option>
<ion-select-option value="3">March</ion-select-option>
</ion-select>
<ion-label>Input w/ clickable</ion-label>
<ion-button slot="end" id="button-end" onclick="setButtonColorRed()">Button</ion-button>
</ion-item>
<ion-item>
<ion-radio slot="start" id="radio-start"></ion-radio>
<ion-label>Input w/ cover + input</ion-label>
<ion-range value="45" id="range-end"></ion-range>
</ion-item>
<ion-item>
<ion-range value="60" id="range-start"></ion-range>
<ion-label>Multiple inputs w/o cover</ion-label>
<ion-toggle id="toggle-1-end"></ion-toggle>
<ion-toggle id="toggle-2-end"></ion-toggle>
</ion-item>
<ion-item button id="clickableItem">
<ion-label>Clickable item</ion-label>
<ion-checkbox slot="end"></ion-checkbox>
</ion-item>
<ion-item>
<ion-checkbox slot="start"></ion-checkbox>
<ion-label>Checkbox w/ disabled button</ion-label>
<ion-button slot="end" onclick="setButtonColorRed()" disabled>Button</ion-button>
</ion-item>
</ion-list>
</ion-content>
</ion-app>
<script>
var ids = ['item', 'datetime', 'select', 'toggle', 'text', 'placeholder', 'checkbox', 'toggle', 'range'];
var isDisabled = false;
const clickableItem = document.querySelector('#clickableItem');
clickableItem.addEventListener('click', function () {
console.log('Clicked item', clickableItem);
const color = clickableItem.color;
clickableItem.color = color === undefined ? 'primary' : undefined;
});
const formControlIds = ['input', 'textarea', 'checkbox', 'toggle', 'select', 'datetime', 'range'];
let isDisabled = false;
const inputs = document.querySelectorAll('ion-input, ion-textarea');
for (var i = 0; i < inputs.length; i++) {
const input = inputs[i];
input.addEventListener('ionBlur', function () {
console.log('Listen ionBlur: fired');
});
input.addEventListener('ionFocus', function () {
console.log('Listen ionFocus: fired');
});
}
function toggleDisabled() {
isDisabled = !isDisabled;
Object.values(getInputs()).forEach((el) => (el.disabled = isDisabled));
Object.values(getFormControls()).forEach((el) => (el.disabled = isDisabled));
}
function setSomeValue() {
const { datetime, select, toggle, text, placeholder, checkbox, range } = getInputs();
text.value = placeholder.value = 'Some text';
const { datetime, select, toggle, input, textarea, checkbox, range } = getFormControls();
input.value = textarea.value = 'Some value';
toggle.checked = checkbox.checked = true;
datetime.value = '2022-04-01T10:00';
range.value = 20;
select.value = 'nes';
select.value = '2';
}
function setEmptyValue() {
const { datetime, select, text, placeholder, range } = getInputs();
text.value = placeholder.value = '';
const { datetime, select, toggle, input, textarea, checkbox, range } = getFormControls();
input.value = textarea.value = '';
toggle.checked = checkbox.checked = false;
datetime.value = '';
range.value = 0;
select.value = '';
}
function setNullValue() {
Object.values(getInputs()).forEach((el) => (el.value = null));
const { toggle, checkbox } = getInputs();
Object.values(getFormControls()).forEach((el) => (el.value = null));
const { toggle, checkbox } = getFormControls();
toggle.checked = checkbox.checked = false;
}
function setUndefinedValue() {
Object.values(getInputs()).forEach((el) => (el.value = undefined));
const { toggle, checkbox } = getInputs();
Object.values(getFormControls()).forEach((el) => (el.value = undefined));
const { toggle, checkbox } = getFormControls();
toggle.checked = checkbox.checked = false;
}
@ -245,21 +164,16 @@
}
function setLabelPosition(position) {
Array.from(document.querySelectorAll('ion-list.basic ion-label')).forEach(
Array.from(document.querySelectorAll('form ion-list ion-label')).forEach(
(label) => (label.position = position)
);
}
function getInputs() {
const elements = {};
for (var i = 0; i < ids.length; i++) {
elements[ids[i]] = document.getElementById(ids[i]);
}
return elements;
}
function testClick(ev) {
console.log('CLICK!', ev.target.tagName, ev.target.textContent.trim());
function getFormControls() {
return formControlIds.reduce((acc, id) => {
acc[id] = document.querySelector(`#${id}`);
return acc;
}, {});
}
function onSubmit(ev) {
@ -271,11 +185,6 @@
document.getElementById('form-result').textContent = JSON.stringify(json);
return false;
}
function setButtonColorRed() {
const button = document.getElementById('button-end');
button.style.setProperty('--background', '#ff0000');
}
</script>
</body>
</html>

View File

@ -1,118 +1,142 @@
import type { Locator } from '@playwright/test';
import { expect } from '@playwright/test';
import type { E2EPage } from '@utils/test/playwright';
import type { EventSpy } from '@utils/test/playwright';
import { test } from '@utils/test/playwright';
test.describe('item: inputs', () => {
test.skip('should not have visual regressions', async ({ page }) => {
let ionPopoverDidPresent: EventSpy;
let ionPopoverDidDismiss: EventSpy;
let formResult: Locator;
let popover: Locator;
test.beforeEach(async ({ page }) => {
await page.goto(`/src/components/item/test/inputs`);
const screenshots = [];
ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent');
ionPopoverDidDismiss = await page.spyOnEvent('ionPopoverDidDismiss');
const disableToggle = page.locator('#btnDisabled');
const submitBtn = page.locator('#submit');
formResult = page.locator('#form-result');
popover = page.locator('ion-popover#optionsPopover');
});
// Check form
await submitBtn.click();
await checkFormResult(
page,
'{"date":"2022-04-01T10:00","select":"n64","toggle":"","input":"","input2":"","checkbox":"","range":"10"}'
);
/**
* We need to expand the viewport so that all the datetime components
* enter the visible viewport. This allows the I/O to fire and
* .datetime-ready to be added.
*/
test('should not have visual regressions', async ({ page }) => {
await page.setIonViewport();
// Wait for all datetime inputs to be ready
await page.waitForSelector('#datetime.datetime-ready');
await page.waitForSelector('#datetime-end.datetime-ready');
// Default case, enabled and no value
screenshots.push({
name: `item-inputs-${page.getSnapshotSettings()}.png`,
screenshot: await captureScreenshot(page),
expect(await page.screenshot()).toMatchSnapshot(`item-inputs-${page.getSnapshotSettings()}.png`);
});
// Disable everything
await disableToggle.click();
test('disabled controls should not have visual regressions', async ({ page }) => {
await page.click('#popover-trigger');
await ionPopoverDidPresent.next();
await page.click('#btnDisabled');
await page.waitForChanges();
// check form
await submitBtn.click();
await checkFormResult(page, '{}');
await popover.evaluateHandle((el: HTMLIonPopoverElement) => el.dismiss());
await ionPopoverDidDismiss.next();
screenshots.push({
name: `item-should-disable-all-${page.getSnapshotSettings()}.png`,
screenshot: await captureScreenshot(page),
await page.setIonViewport();
expect(await page.screenshot()).toMatchSnapshot(`item-inputs-disabled-${page.getSnapshotSettings()}.png`);
});
// Reenable and set some value
await disableToggle.click();
await page.click('#btnSomeValue');
test.describe('form data', () => {
// eslint-disable-next-line no-empty-pattern
test.beforeEach(async ({}, testInfo) => {
test.skip(testInfo.project.metadata.rtl === true, 'Does not test LTR vs. RTL layout.');
});
// check form
await submitBtn.click();
await checkFormResult(
page,
'{"date":"2022-04-01T10:00","select":"nes","toggle":"on","input":"Some text","input2":"Some text","checkbox":"on","range":"20"}'
test('initial form data should be empty', async ({ page }) => {
await page.click('#submit');
await expect(await formResult.textContent()).toEqual(
'{"input":"","textarea":"","toggle":"","checkbox":"","select":"","datetime":"2022-04-01T10:00","range":"10"}'
);
screenshots.push({
name: `item-should-reenable-and-set-value-${page.getSnapshotSettings()}.png`,
screenshot: await captureScreenshot(page),
});
// Set "null"
await page.click('#btnNullValue');
test('form controls have some value', async ({ page }) => {
await page.click('#popover-trigger');
await ionPopoverDidPresent.next();
screenshots.push({
name: `item-should-set-null-${page.getSnapshotSettings()}.png`,
screenshot: await captureScreenshot(page),
});
// Set "empty"
await page.click('#btnEmptyValue');
screenshots.push({
name: `item-should-set-empty-${page.getSnapshotSettings()}.png`,
screenshot: await captureScreenshot(page),
});
// Test multiple
await page.click('#checkbox-start');
await page.click('#datetime-end');
screenshots.push({
name: `item-should-check-checkbox-and-open-datepicker-${page.getSnapshotSettings()}.png`,
screenshot: await captureScreenshot(page),
});
await page.click('#button-end');
await page.click('#btnSomeValue');
await page.waitForChanges();
screenshots.push({
name: `item-should-change-button-color-to-red-${page.getSnapshotSettings()}.png`,
screenshot: await page.screenshot(),
await popover.evaluateHandle((el: HTMLIonPopoverElement) => el.dismiss());
await ionPopoverDidDismiss.next();
await page.click('#submit');
await expect(await formResult.textContent()).toEqual(
'{"input":"Some value","textarea":"Some value","toggle":"on","checkbox":"on","select":"2","datetime":"2022-04-01T10:00","range":"20"}'
);
});
for (const screenshot of screenshots) {
expect(screenshot.screenshot).toMatchSnapshot(screenshot.name);
}
test('form control values set to be empty', async ({ page }) => {
await page.click('#popover-trigger');
await ionPopoverDidPresent.next();
await page.click('#btnEmptyValue');
await page.waitForChanges();
await popover.evaluateHandle((el: HTMLIonPopoverElement) => el.dismiss());
await ionPopoverDidDismiss.next();
await page.click('#submit');
await expect(await formResult.textContent()).toEqual(
'{"input":"","textarea":"","toggle":"","checkbox":"","select":"","datetime":"","range":"0"}'
);
});
test('form control values set to null', async ({ page }) => {
await page.click('#popover-trigger');
await ionPopoverDidPresent.next();
await page.click('#btnNullValue');
await page.waitForChanges();
await popover.evaluateHandle((el: HTMLIonPopoverElement) => el.dismiss());
await ionPopoverDidDismiss.next();
await page.click('#submit');
await expect(await formResult.textContent()).toEqual(
'{"input":"","textarea":"","toggle":"","checkbox":"","select":"","datetime":"","range":"0"}'
);
});
test('form control values set to undefined', async ({ page }) => {
await page.click('#popover-trigger');
await ionPopoverDidPresent.next();
await page.click('#btnUndefinedValue');
await page.waitForChanges();
await popover.evaluateHandle((el: HTMLIonPopoverElement) => el.dismiss());
await ionPopoverDidDismiss.next();
await page.click('#submit');
await expect(await formResult.textContent()).toEqual(
'{"input":"","textarea":"","toggle":"","checkbox":"","select":"","datetime":"","range":"0"}'
);
});
test('should not have form data when controls are disabled', async ({ page }) => {
await page.click('#popover-trigger');
await ionPopoverDidPresent.next();
await page.click('#btnSomeValue');
await page.click('#btnDisabled');
await page.waitForChanges();
await popover.evaluateHandle((el: HTMLIonPopoverElement) => el.dismiss());
await ionPopoverDidDismiss.next();
await page.click('#submit');
await expect(await formResult.textContent()).toEqual('{}');
});
});
});
const checkFormResult = async (page: E2EPage, content: string) => {
const div = page.locator('#form-result');
await expect(await div.textContent()).toEqual(content);
};
/**
* Resizes the viewport and captures a screenshot.
* Required for this test suite, since the DOM size is not
* the same at each test case.
*/
const captureScreenshot = async (page: E2EPage) => {
await page.setIonViewport();
return page.screenshot();
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 KiB

After

Width:  |  Height:  |  Size: 98 KiB