2

To do some tests, I need to set fields to a value. I'm having some trouble to set a droplist the best way. Other suites seem to already start before a suite with the droplist is done. I'm looping through fields, and per field made a suite, specifically to ensure they are not done parallel. The suite sets and checks the field.

For the droplist (quasar q-select) the situation is I don't know how many options there are, and after n options, new options are added to the selection content and old ones removed. This means if you just select the div which contains all the options, it will work if you value would be in that amount.

Right now I have this custom function for a droplist. Each time you press the arrow down, the selection moves down, and when needed it will add the next options to the list. Hence I keep need to get the content.

Cypress.Commands.add('selectOption', (strOptionValue) => {
    function funcSelect (strOptionValue, iIndex) {
        cy.get('.q-select__options--content')
            .find('.q-item')
            .eq(iIndex)
            .then($oOption => {
                cy.wrap($oOption)
                    .find('[class=q-item__label]')
                    .then($oOptionLabel => {
                        const strListValue = $oOptionLabel.text();

                        cy.log('['+strOptionValue+']-['+strListValue+']');

                        if (strListValue === strOptionValue) {
                            cy.wrap($oOption)
                                .click();
                        } else {
                            cy.wrap($oOption)
                                .type('{downarrow}');

                            funcSelect(strOptionValue, iIndex + 1);
                        }
                    });
            });
    }

    funcSelect(strOptionValue, 0);
});

What I think is not right: using the index. The amount doesn't change in the list, and now it keeps checking the whole list. I think it should somehow be possible to do:

  1. loop the list
  2. if not found at the last one, click down
  3. get the list,
  4. get the item from step 2
  5. check the next
  6. repeat till the last one etc

Any ideas here?

EDIT: There are two additional challenges: a. The start is sometimes at the middle of the total amount of items. The arrow down, at the last item moves to the first. This is okay, but it means a break out needs to be on the item where you first started too. b. The 'arrowdown' does the same as the click, it selects the item. I'm still checking, but it might cause a problem in continuing with the test cycle before the actual value is clicked

1
  • From (a) I assume it's not an "infinite list" which was my first assumption, and (b) is a killer for your algorithm. There must be a way for the user to move the list without selecting, otherwise there would be no way to have more than the original list available. If the user must use to scrollbar, that's going to be difficult to do in javascript/Cypress. Commented Mar 29, 2022 at 19:26

1 Answer 1

3

I think you're on the right track with the recursive function, but it needs a break-out guard to stop infinite recursion.

Then I'd get all existing option labels that Cypress can currently pick up (i.e loaded into DOM) and see if the search value is in the list.

Since the list changes each recursion, I would re-query the option inside the if/else rather than wrapping.

Lastly I would chain the recursion call inside a .then(), since .type('{downarrow}') happens on the Cypress command queue and you only want to move to next call after that step finishes. You may also want to use a small wait or use an intercept wait if the items are fetched from an API.

function selectOption(searchForValue, attempt = 0) {

  if (attempt > 10 ) throw 'Too many attempts - item not found';

  cy.get('.q-select__options--content')
    .find('[class=q-item__label]')
    .then($labels => {
      const listValues = [...$labels].map(label => label.innerText)
      if (listValues.includes(searchForValue) {

        cy.contains('.q-select__options--content .q-item', searchForValue)
          .click()

      } else {

        cy.contains('.q-select__options--content .q-item', searchForValue)
          .type('{downarrow}')
          .then(() => {
            cy.wait(100) // or wait on an intercept alias if API involved
            selectOption(searchForValue, attempt + 1)
          })
      }
    })
}

selectOption(searchForValue);

BTW I think func and str prefixes were officially deprecated in 1996 ;).


Overcoming problem with down arrow

Based on your comment about down-arrow, I tried it out on the "Standard" quasar select with 1000 items.

Some of the selectors needed adjusting, but the principle works.

function selectOption(searchValue, attempt = 0) {

  if (attempt > 100 ) throw 'Too many attempts - item not found';

  cy.get('.q-item__label')
    .then($labels => {
      const listValues = [...$labels].map(label => label.innerText)

      if (listValues.includes(searchValue)) {
        cy.contains('.q-item__label', searchValue).click()
      } else {

        const lastItem = listValues[listValues.length -1]
        cy.contains('.q-item__label', lastItem)
          .type('{downarrow}')
          .then(() => {
            selectOption(searchValue, attempt + 1)  // next batch
          })
      }
    })
}

const searchValue = '500'

cy.get('.q-field__append').click();         // open dropdown

selectOption(searchValue);                  // find value

cy.get('.q-field__control-container span')  // confirm value
  .should('have.text', searchValue)         // passes ✅
Sign up to request clarification or add additional context in comments.

4 Comments

Offcourse, creation of an array of list values is way easier. You options do help here. Since the post, I've two more 'challenges' in the story, I'll put them in the post
Thanks, this seems sufficient. I marked it as answer. Going to throw it against a complete application test tomorrow I should get less failures it seems
It indeeds works. I made an addional adjustment. Contains itself doesn't cover it, I prefer exact matching so I used a regex, naturally escaping the characters. I need to do all this for users can change the list values and the test at radom validates a few.
the deprecation not on Func prefixes is important :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.