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

Select / highlight text #2839

Open
ithacasnowman opened this issue Nov 26, 2018 · 23 comments
Open

Select / highlight text #2839

ithacasnowman opened this issue Nov 26, 2018 · 23 comments
Labels
E2E Issue related to end-to-end testing existing workaround pkg/driver This is due to an issue in the packages/driver directory stage: proposal 💡 No work has been done of this issue type: feature New feature that does not currently exist

Comments

@ithacasnowman
Copy link

ithacasnowman commented Nov 26, 2018

Current behavior:

I'm having a hard time creating a text selection. Things I've tried:

  • document.createRange() and window.getSelection()
  • Using cy.trigger to simulate a cursor drag over the paragraph.
  • Using dblclick on the paragraph.

Desired behavior:

Test highlighting text.

Steps to reproduce:

I can provide sample code for the approaches I tried, but I'll hold off for now in case there's a completely different known way of doing this.

Versions

Cypress 3.1.1, Chrome 70.0.3538.102

@lilaconlee
Copy link
Contributor

What are you trying to test with highlighting text? It'll be easier to determine a fix if we understand what you're trying to do.

@ithacasnowman
Copy link
Author

My application keeps a record of highlighted text. I'm trying to test that that works correctly.

@jennifer-shehane
Copy link
Member

jennifer-shehane commented Dec 7, 2018

We've gotten this question several times. We don't have a specific API implemented to handle highlighting text. We do have a generic cy.trigger() where you can trigger any event, so it is theoretically possible to test many of what you'd need, but would require intimate knowledge of how your application works and what events it triggers.

I will update this issue to reflect request for this feature. This issue should now track:

  • Proposals for how users would like to cypress commands work to test 'highlight' text
  • Current workarounds for testing highlighting text.
  • Any PRs/progress made for the implementation.

@jennifer-shehane jennifer-shehane changed the title Question: how can I select text in a paragraph? Select / highlight text in a paragraph Dec 7, 2018
@jennifer-shehane jennifer-shehane added type: feature New feature that does not currently exist stage: proposal 💡 No work has been done of this issue pkg/driver This is due to an issue in the packages/driver directory difficulty: 4️⃣ labels Dec 7, 2018
@jennifer-shehane
Copy link
Member

This feature also requires work that's already in progress for our Native Events issue: #311

@jennifer-shehane
Copy link
Member

jennifer-shehane commented Dec 11, 2018

Related comment from @tnrich on wanting ability to highlight text with cy.type('{meta}a'):

I would specifically like a way to trigger the exact same thing as a user-invoked "meta+a" on any element. That might happen to select text in some instances, or select everything in the page if not inside an input/textarea, or it might do something totally different if there are hotkeys listening to that specific keypress.

@bkucera Would this situation be covered in the Native Events work currently?

@kuceb
Copy link
Contributor

kuceb commented Dec 13, 2018

This would have to be a new command entirely, and isn't planned for the native events release. However we do plan to support text selection in the future

In the meantime, you can try this:

cy.get('p.mytext')
.trigger('mousedown')
.then(($el) => {
  const el = $el[0]
  const document = el.ownerDocument
  const range = document.createRange()
  range.selectNodeContents(el)
  document.getSelection().removeAllRanges(range)
  document.getSelection().addRange(range)
})
.trigger('mouseup')
cy.document().trigger('selectionchange')

@vctormb
Copy link

vctormb commented Apr 5, 2019

@bkucera why this line cy.document().trigger('selectionchange') is needed? Btw, great solution!

@erquhart
Copy link

erquhart commented Apr 11, 2019

Here are a couple of commands that are working for my team, using Cypress to test a Slate based editor:
https://gist.github.com/erquhart/37bf2d938ab594058e0572ed17d3837a

Includes the ability to naively select based on text matching:

cy.get('p').setSelection('foo')

Or set both the start and end points of the selection:

cy.get('p').setSelection('foo', 'baz')

Also setCursorBefore/After with the same text matching interface, plus some other lower level commands.

@kuceb
Copy link
Contributor

kuceb commented Apr 12, 2019

@vctormb it's what the browser fires if you were to select text manually, but it won't make a difference if your app code doesn't listen for it

@jennifer-shehane jennifer-shehane changed the title Select / highlight text in a paragraph Select / highlight text Jan 21, 2020
@samtsai
Copy link
Contributor

samtsai commented Mar 10, 2020

FYI, I couldn't get the gist above working with input or textarea's and it looks like its due to lack of support: https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection#Related_objects (it also wasn't working in Chrome 80 for me), so I created a fork of the gist with a different path for those elements:
https://gist.github.com/samtsai/5cf901ba61fd8d44c8dd7eaa728cac49

@VicLim4
Copy link

VicLim4 commented Jul 28, 2020

@erquhart Thanks you so much !!! The highlighting works !!!

I feel maybe this can be made into an official way of highlighting text instead of just being labelled as a workaround !!! hehe

@Anahkiasen
Copy link

Yeah I used this Gist successfully on a selection-heavy project test suite and encountered no real issue, I'd be fine if this was the code used for official support despite its potential limitations, it's still better than no support

@erquhart
Copy link

erquhart commented Aug 6, 2020

Glad it worked for you! It's super naive, Eg., duplicates of the targeted string can trip it up easily. We've avoided this by not using duplicate strings in our tests (they're generally short strings anyway). Probably not a viable candidate for a first class solution, but a good stopgap.

For anyone running into issues w/ the gist, our implementation has evolved a bit and may work better. You'll just have to dig a bit in the source file to find the relevant code. Here's the source as of today: https://github.com/netlify/netlify-cms/blob/a4b7481a99f58b9abe85ab5712d27593cde20096/cypress/support/commands.js#L180

@armaaar
Copy link

armaaar commented Aug 14, 2020

@erquhart Thanks a lot! You saved us hours of work.

I really recommend you to create a separate package with all selection related commands so people can just install it via npm in the future and use it directly. I'd be the first one to star it!

@dzschille
Copy link

For future readers: if @erquhart solution is not working for you, try to trigger a 'selectstart' after the triggered 'mousedown' in the 'selection' command. That seems to fake better a real mouse interaction.
In my case i had a popup that is triggered when text was selected.

@manuelmeister
Copy link

@jennifer-shehane how can we move this issue forward?

@NZhlebinkov
Copy link

If the previous workarounds didn't work for some people (like me), you could try cy.realPress(["Shift", "ArrowLeft"]); from https://github.com/dmtrKovalenko/cypress-real-events

@Wolsten
Copy link

Wolsten commented Aug 24, 2021

@erquhart What a great solution and many thanks for sharing. I have similar issues starting out testing my own rich text editor app and this saved me a lot of time.

@Kname80
Copy link

Kname80 commented Jan 19, 2022

Any other suggestions for handling selecting text inside iframe ?

@thednp
Copy link

thednp commented May 18, 2022

@erquhart thanks for your solution!! Here's a simple fork to select anything within a Node. No need to change Cypress.command or anything, just a simple fixed utility function.

Code

/**
 * Returns an `Array` with all `#text` nodes in a Node. 
 * @param {Node} target root node
 * @returns {Node[]} the requested nodes
 */
function getTextNodes(target) {
  if (target.nodeType === 3) {
    return [target];
  }

  let nodes = [];
  const doc = target.ownerDocument;
  const win = doc.defaultView;
  const walk = doc.createTreeWalker(target, win.NodeFilter.SHOW_TEXT, null);
  let node;

  while (node = walk.nextNode()) {
    if (node.nodeType === 3) {
      nodes = [...nodes, node];
    }
  }
  return nodes;
}

/**
 * Select all the text of the first textNode of a given element.
 * @param {Node} target target element
 */
export default function selectText(target) {
  const textNodes = getTextNodes(target);
  const [startNode, endNode] = [...textNodes.slice(0,1), ...textNodes.slice(-1)];
  console.log(startNode,endNode)
  const doc = target.ownerDocument;
  const win = doc.defaultView;

  const range = new win.Range();
  range.setStart(startNode , 0);
  range.setEnd(endNode, endNode.textContent.length);
  win.getSelection().removeAllRanges();
  win.getSelection().addRange(range);
}

How to

  • Create a file cypress/fixtures/selectText.js and paste the above code
  • In your cypress/integration/FILE.spec.js use import selectText from '../fixtures/selectText'
  • Then your "testing workflow" goes like this
it('selects all text in a Node', 90 => {
  cy.get('SELECTOR').then((target) => {
    selectText($target[0]);
    // now do something with all that selected text
    expect(target[0].ownerDocument.getSelection().toString().length).to.be.above(0);
  })
})

Last but not least: have fun!

@blabute
Copy link

blabute commented Jun 6, 2023

If the previous workarounds didn't work for some people (like me), you could try cy.realPress(["Shift", "ArrowLeft"]); from https://github.com/dmtrKovalenko/cypress-real-events

This worked like a charm! Thanks @NZhlebinkov

@nagash77 nagash77 added the E2E Issue related to end-to-end testing label Jun 7, 2023
@rahulworks-git
Copy link

rahulworks-git commented Oct 9, 2023

I want to select and highlight span all the elements present under the

element. After all the element under the mentioned tag are selected, we get a word "Santa" that is highlighted.
None of the workarounds mentioned above seem to work. Anyone is aware how can this be done?

<div class="section-text">
    <span sid="0" ost="0">S</span>
    <span sid="0" ost="1">a</span>
    <span sid="0" ost="2">n</span>
    <span sid="0" ost="3">t</span>
    <span sid="0" ost="4">a</span>

@Alex20180512
Copy link

Cypress.Commands.add('selectText', function(text) {
  cy.contains(text).then(($el) => {
    const el = $el[0];
    const document = el.ownerDocument;
    
    const range = document.createRange();
    range.selectNodeContents(el);

    const fullText = el.textContent || "";
    const startIndex = fullText.indexOf(text);
    const endIndex = startIndex + text.length;

    if (startIndex !== -1 && endIndex !== -1) {
      range.setStart(el.firstChild, startIndex);
      range.setEnd(el.firstChild, endIndex);

      const selection = document.getSelection();
      selection.removeAllRanges();
      selection.addRange(range);

      $el.trigger('mouseup');
      cy.document().trigger('selectionchange');
    } else {
      throw new Error(`The text "${text}" was not found in the element`);
    }
  });
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
E2E Issue related to end-to-end testing existing workaround pkg/driver This is due to an issue in the packages/driver directory stage: proposal 💡 No work has been done of this issue type: feature New feature that does not currently exist
Projects
None yet
Development

No branches or pull requests