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: Place the caret at the start/end (all text elements) or clicked point (contenteditable only) #8529

Closed
wants to merge 7 commits into from

Conversation

sainthkh
Copy link
Contributor

@sainthkh sainthkh commented Sep 9, 2020

User facing changelog

Users can choose to place the caret at the start or end of the text elements(<input>, <textarea>)

When a contentdeditable element is clicked, the caret can be placed at the clicked point.

Additional details

  • Why was this change necessary? => Some users wanted to test their contenteditable is edited correctly but it was impossible.
  • What is affected by this change? => N/A
  • Any implementation details to explain? => I used caretPositionFromPoint and caretRangeFromPoint. Because of that, it only works with [contenteditable].

How has the user experience changed?

N/A

PR Tasks

  • Have tests been added/updated?
  • Has the original issue been tagged with a release in ZenHub?
  • Has a PR for user-facing changes been opened in cypress-documentation? => coming soon...
  • Have API changes been updated in the type definitions?
  • [N/A] Have new configuration options been added to the cypress.schema.json?

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Sep 9, 2020

Thanks for taking the time to open a PR!

@sainthkh sainthkh force-pushed the issue-7721 branch 2 times, most recently from 780a35c to 312ddda Compare September 10, 2020 09:14
@sainthkh sainthkh marked this pull request as ready for review September 10, 2020 09:36
@sainthkh
Copy link
Contributor Author

Flaky failures.

Comment on lines 889 to 890
// caretPositionFromPoint doesn't work on headless mode on Firefox.
if (Cypress.isBrowser('chrome')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should try to fix this not working in headless firefox. This could be really confusing to users whose tests fail in runMode but pass in openMode.

If we were going to ship this we would need to print a warning in headless firefox if the option is supplied.

We also would need to write some tests to cover the behavior in headed firefox

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cause of the problem is caretPositionFromPoint function. It simply returns 0 in headless mode. I fixed this test by adding Cypress.config('isInteractive') === true.

I added error message about skipping this test.

@@ -2393,6 +2393,13 @@ declare namespace Cypress {
* @default false
*/
cmdKey: boolean
/**
* Where the caret should be placed when <input>, <textarea>, [contenteditable] elements are clicked.
* 'point' option can be only used with [contenteditable].
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add some logic to throw an error if caretPosition: 'point' is given to a non [contenteditable] element

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an error message.

Comment on lines +494 to +498
} else if (caretPosition === 'point') {
const { x, y } = getMouseCoords(state)

$selection.moveSelectionToPoint(state('window'), x, y)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly needs onlyIfEmptySelection: true to fulfill the browser default action if selection is changed during the prev event

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that it's impossible. To use onlyIfEmptySelection, We need element. But in this case, we don't use element. We just use clientX and clientY.

@jennifer-shehane
Copy link
Member

What happened to the tests :(

@jennifer-shehane
Copy link
Member

@sainthkh I'm not sure what's happened to the tests in here - seems specific to this PR.

@sainthkh
Copy link
Contributor Author

sainthkh commented Oct 7, 2020

I'll check it out tomorrow.

@sainthkh
Copy link
Contributor Author

sainthkh commented Oct 8, 2020

@jennifer-shehane The bug happened because of a circular reference in files. I'm finding ways to solve that problem.

Copy link
Contributor

@kuceb kuceb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

considering this proposed solution does not work in firefox headless, I think there's a better solution here.

Right now we grab the host contenteditable if the subject is inside a contenteditable. Users want the ability to target specific elements inside, not necessarily where the click hits.

So we could change behavior when targeting contenteditables to select the provided subject instead of the host contenteditable. so if the user does cy.get('div[contenteditable] > div > p') we put the cursor at the end of <p> instead of div[contenteditable]. This could use the simple selection.selectNodeContents api to achieve that. Do you think that would meet users' needs @sainthkh? It would be a breaking change

@sainthkh
Copy link
Contributor Author

@bkucera It's yes and no.

I think #5721 isn't an invalid request because there are cases like putting the caret at a precise position is necessary to test WYSIWYG editors. (Example scenario: Click a position in the middle of a sentence. Ctrl + b to turn on bold mode. Type a word. Check if the new word is shown in bold.)

And I'm also afraid that breaking cy.get() is too risky because it's used a lot. Maybe there are a lot of users who are comfortable with the current behavior.

My answer for your suggestion is this:

Add selectRootContenteditable option to get. Default is true. If someone wants to select something with elements inside [contenteditable] set it to false. It would solve #2717 and #7721.

The problem is #5721. For some developers, current 'point' option is definitely better than nothing. But it's a half-done feature because it doesn't work on Firefox headless (in exact words, when Firefox GUI cannot be executed).

I believe there are 3 options for now:

  1. Release it. Document and throw error when used with Firefox.
  2. Discard it. And revisit this when the bug in caretPositionFromPoint is fixed.
  3. Release it as a plugin. And maybe merge this to this repo when the bug is fixed.

What do you think?

@kuceb
Copy link
Contributor

kuceb commented Oct 20, 2020

@sainthkh this would not affect cy.get, only what element cy.type and cy.click target, so it wouldn't be that big of a breaking change. cy.type already has logic around contenteditable subjects

as for #5721 I dont believe this PR fixes the issue. That issue is asking for an option like { cursorPosition: 3 }, not the clicked point. I don't believe using the clicked point is not precise or predictable enough for testing. For example a simple padding change could move the click from character 3 to character 4, since we target the center of the element by default.

@sainthkh
Copy link
Contributor Author

@bkucera I agree with you. I'll close this PR and open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants