Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use 'fill' for inputs #727

Merged
merged 16 commits into from
Jul 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,21 @@ npx qawolf create url [name]

💪 Convert your actions into [Playwright](https://github.com/microsoft/playwright) code:

| Action | Status | Example |
| ------------------------------------------------------------ | :----: | ---------------------------------------------------------- |
| Click | ✅ | `page.click(selectors['0_submit'])` |
| Type | ✅ | `page.type(selectors['0_username'], 'username')` |
| Scroll | ✅ | `qawolf.scroll(page, 'html', { x: 0, y: 200 })` |
| Select | ✅ | `page.selectOption(selectors['0_ice_cream'], 'chocolate')` |
| Replace text | ✅ | `page.fill(selectors['0_username'], 'username')` |
| Paste | ✅ | `page.type(selectors['password'], 'pasted')` |
| Use a test attribute | ✅ | `page.click("[data-qa='submit']")` |
| Use a test attribute on an ancestor | ✅ | `page.click("[data-qa='radio'] [value='cat']")` |
| Use multiple pages/tabs | ✅ | `qawolf.waitForPage(page.context(), 1)` |
| [Iframes](https://github.com/qawolf/qawolf/issues/279) | 🗺️ | Coming soon |
| [Drag and drop](https://github.com/qawolf/qawolf/issues/315) | 🗺️ | Coming soon |
| [File upload](https://github.com/qawolf/qawolf/issues/331) | 🗺️ | Coming soon |
| [Back button](https://github.com/qawolf/qawolf/issues/438) | 🗺️ | Coming soon |
| Action | Status | Example |
| ------------------------------------------------------------ | :----: | ----------------------------------------------- |
| Click | ✅ | `page.click('#login')` |
| Type | ✅ | `page.fill('.username', 'spirit@qawolf.com')` |
| Scroll | ✅ | `qawolf.scroll(page, 'html', { x: 0, y: 200 })` |
| Select | ✅ | `page.selectOption('.ice_cream', 'chocolate')` |
| Replace text | ✅ | `page.fill('.username', 'username')` |
| Paste | ✅ | `page.type('password', 'pasted')` |
| Use a test attribute | ✅ | `page.click("[data-qa='submit']")` |
| Use a test attribute on an ancestor | ✅ | `page.click("[data-qa='radio'] [value='cat']")` |
| Use multiple pages/tabs | ✅ | `qawolf.waitForPage(page.context(), 1)` |
| [Iframes](https://github.com/qawolf/qawolf/issues/279) | 🗺️ | Coming soon |
| [Drag and drop](https://github.com/qawolf/qawolf/issues/315) | 🗺️ | Coming soon |
| [File upload](https://github.com/qawolf/qawolf/issues/331) | 🗺️ | Coming soon |
| [Back button](https://github.com/qawolf/qawolf/issues/438) | 🗺️ | Coming soon |

As your test is created:

Expand Down
39 changes: 36 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
},
"devDependencies": {
"@ffmpeg-installer/ffmpeg": "^1.0.20",
"@qawolf/sandbox": "0.1.12",
"@qawolf/sandbox": "0.1.13",
"@types/debug": "^4.1.5",
"@types/fs-extra": "^9.0.1",
"@types/glob": "^7.1.3",
Expand All @@ -89,6 +89,7 @@
"rimraf": "^3.0.2",
"ts-jest": "^26.1.1",
"ts-loader": "^7.0.5",
"ts-node": "^8.10.2",
"typescript": "^3.9.6",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
Expand Down
2 changes: 1 addition & 1 deletion packages/sandbox/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/sandbox/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@qawolf/sandbox",
"version": "0.1.12",
"version": "0.1.13",
"files": [
"bin",
"build"
Expand Down
5 changes: 5 additions & 0 deletions packages/sandbox/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import LogIn from './pages/LogIn';
import NestedDataAttributes from './pages/NestedDataAttributes';
import RadioInputs from './pages/RadioInputs';
import Selects from './pages/Selects';
import SpecialInputs from './pages/SpecialInputs';
import TextInputs from './pages/TextInputs';
import TimePickers from './pages/TimePickers';
// CSS
Expand Down Expand Up @@ -58,6 +59,9 @@ function Navigation() {
<li>
<Link to="/selects">Selects</Link>
</li>
<li>
<Link to="/special-inputs">Special inputs</Link>
</li>
<li>
<Link to="/text-inputs">Text inputs</Link>
</li>
Expand Down Expand Up @@ -87,6 +91,7 @@ function App() {
/>
<Route component={RadioInputs} path="/radio-inputs" />
<Route component={Selects} path="/selects" />
<Route component={SpecialInputs} path="/special-inputs" />
<Route component={TextInputs} path="/text-inputs" />
<Route component={TimePickers} path="/time-pickers" />
<Redirect to="/" />
Expand Down
5 changes: 5 additions & 0 deletions packages/sandbox/src/pages/DatePickers/HtmlDatePickers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ function HtmlDatePickers() {
return (
<div className="container">
<h3>Native HTML</h3>
<h4>Date</h4>
<input data-qa="html-date-picker" type="date" />
<h4>Week</h4>
<input data-qa="html-week-picker" type="week" />
<h4>Month</h4>
<input data-qa="html-month-picker" type="month" />
</div>
);
}
Expand Down
15 changes: 15 additions & 0 deletions packages/sandbox/src/pages/SpecialInputs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";

function SpecialInputs() {
return (
<div className="container">
<h3>Native HTML</h3>
<h4>Color</h4>
<input data-qa="html-color-picker" type="color" />
<h4>Range</h4>
<input data-qa="html-range-picker" type="range" min="0" max="10" />
</div>
);
}

export default SpecialInputs;
12 changes: 12 additions & 0 deletions packages/sandbox/src/pages/TextInputs/HtmlTextInputs.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ function HtmlTextInputs() {
/>
<br />
<br />
<input data-qa="html-search-input" placeholder="Search input" type="search" />
<br />
<br />
<input data-qa="html-email-input" placeholder="Email input" type="email" />
<br />
<br />
<input data-qa="html-url-input" placeholder="URL input" type="url" />
<br />
<br />
<input data-qa="html-tel-input" placeholder="Telephone input" type="tel" />
<br />
<br />
<input
data-qa="html-password-input"
placeholder="Password input"
Expand Down
3 changes: 3 additions & 0 deletions packages/sandbox/src/pages/TimePickers/HtmlTimePickers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ function HtmlTimePickers() {
return (
<div className="container">
<h3>Native HTML</h3>
<h4>Time</h4>
<input data-qa="html-time-picker" type="time" />
<h4>Local date and time</h4>
<input data-qa="html-datetime-local-picker" type="datetime-local" />
</div>
);
}
Expand Down
8 changes: 3 additions & 5 deletions src/build-workflow/buildClickSteps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Debug from 'debug';
import { ElementEvent, Step } from '../types';
import { isInputTarget } from './target';

const debug = Debug('qawolf:buildClickSteps');

Expand All @@ -17,7 +18,7 @@ const filterClickEvents = (events: ElementEvent[]): ElementEvent[] => {
const previousEvent = events[i - 1];
if (
previousEvent &&
['keydown', 'keyup'].includes(previousEvent.name) &&
['change', 'keydown', 'keyup'].includes(previousEvent.name) &&
event.time - previousEvent.time < 50
) {
// skip system-initiated clicks triggered by a key press
Expand Down Expand Up @@ -75,10 +76,7 @@ export const buildClickSteps = (events: ElementEvent[]): Step[] => {
groupedClickEvents.forEach((events) => {
let event = events[0] as ElementEvent;

const inputEvent = events.find((event) => {
const name = event.target.name || '';
return name.toLowerCase() === 'input';
});
const inputEvent = events.find((event) => isInputTarget(event.target));
if (inputEvent) {
// if an event in the group is on an input, assume the click propagated
// to an element like a checkbox or radio, which is most accurate target
Expand Down
67 changes: 67 additions & 0 deletions src/build-workflow/buildFillSteps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import Debug from 'debug';
import {
ElementEvent,
InputEvent,
Step,
} from '../types';
import { isChangeEvent, isInputEvent } from './event';
import { isContentEditableTarget, isInputTarget, isTextareaTarget } from './target';

const debug = Debug('qawolf:buildFillSteps');

const shouldFill = (event: ElementEvent): boolean => {
const { target } = event;
return (isInputEvent(event) || isChangeEvent(event)) &&
(isInputTarget(target) || isTextareaTarget(target) || isContentEditableTarget(target)) &&
// Some inputs emit "change" with a value but really can't or shouldn't be
// "filled in" with that value. Checkbox and radio should work without filling
// because there will be click events. File isn't supported.
![
'checkbox',
'radio',
'file'
].includes(target.attrs && target.attrs.type);
}

/**
* @summary Given a list of captured browser page events, returns a list of input fill
* steps that should be included when playing back the flow for testing purposes.
*/
export const buildFillSteps = (events: ElementEvent[]): Step[] => {
debug('building fill steps');

const fillSteps = [];
let lastInputEvent: ElementEvent;
for (let index = 0; index < events.length; index++) {
const event = events[index];
if (isInputEvent(event)) {
lastInputEvent = event;
}
if (lastInputEvent && shouldFill(event)) {
// We use change events to determine when an input fill needs to happen
// but they always happen AFTER the Tab, Enter, click, etc. that caused
// the change to be committed. So instead of attaching the change event
// to the fill, we attach the most recent input event, which happened
// before the keydown or click.
const { value } = event as InputEvent;
// Also most inputs that have masks tend to fire "change" event multiple
// times while you are typing in the mask. We only want the final fill value,
// so if this fill is identical to the previous fill other than the value,
// we'll overwrite the previous.
const previousFill = fillSteps[fillSteps.length - 1];
if (previousFill && previousFill.event.selector === lastInputEvent.selector) {
previousFill.event = lastInputEvent;
previousFill.value = value;
} else {
fillSteps.push({
action: 'fill',
event: lastInputEvent,
index: fillSteps.length,
value,
});
}
}
}

return fillSteps;
};
Loading