[FIXED] Cypress with an Ionic app: cannot access ion-select-option because it is "not visible"

Issue

I have a have an ionic/angular app which contains a particular form-control as follows:

<ion-select #typeLst interface="alert" name="typeCode" formControlName="typeCode" okText="OK" cancelText="Dismiss" placeholder="Choose a firm type">
      <ion-select-option value="EPFIRMI001">Informal</ion-select-option>
      <ion-select-option value="EPFIRMF001">Formal/Ltd.</ion-select-option>
 </ion-select>

In my cypress test, I can navigate to the page and do other things on it but when I try to select a specific item on the "select" cypress is ‘aware’ of it but cannot access it.

Cypress code:

cy.get('ion-select[name="typeCode"]').click()

cy.get('ion-select-option[value="EPFIRMF001"]').click()

Cypress error:

Timed out retrying: cy.click() failed because this element is not visible:

<ion-select-option _ngcontent-dqd-c137="" value="EPFIRMF001" ng-reflect-value="EPFIRMF001" role="option" id="ion-selopt-1" class="md hydrated">Formal/...</ion-select-option>

This element <ion-select-option#ion-selopt-1.md.hydrated> is not visible because it has CSS property: display: none

Fix this problem, or use `{force: true}` to disable error checking.

N.B:

  • changing the interface type of the ion-select (e.g. to ‘popover’) makes a slight difference as follows:
    • using {force: true} + interface='popover' allows Cypress to "pass" this step but it does not actually "click" the element, so the Select list remains in foreground and the next step fails.
    • using {force: true} + interface='alert' allows Cypress to "pass" this step and the Select list is backgrounded, however the select option is not actually "selected", so the app is now in an incorrect state for the next step.

Versions:

  • Angular: 9.1.9
  • Browser: Chrome 79.0.3945.117
  • Cypress Version: 4.9.0
  • Ionic version: 6.9.3
  • O/S: LinuxMint 20

EDIT 09/08/2020
In response (too long for a comment) to @SanjaPaskova’s second suggestion:
Using select as follows: cy.get('ion-select[name="typeCode"]').select('Formal')
…produces following error message:

cy.select() can only be called on a <select>. Your subject is a: <ion-select _ngcontent-jql-c137="" interface="alert " name="typeCode" formcontrolname="typeCode" oktext="OK" canceltext="Dismiss" placeholder="Choose a firm type" ng-reflect-interface="alert " ng-reflect-name="typeCode" ng-reflect-ok-text="OK" ng-reflect-cancel-text="Dismiss" ng-reflect-placeholder="Choose a firm type" class="ng-untouched ng-pristine ng-valid ion-untouched ion-pristine ion-valid md in-item hydrated" role="combobox" aria-haspopup="dialog" aria-expanded="true" aria-labelledby="ion-sel-0-lbl">...</ion-select>

Ionic/Angular source is above in my original post. This produces the below generated DHTML:

<ion-card-content _ngcontent-nld-c140="" class="md card-content-md hydrated"><form _ngcontent-nld-c140="" novalidate="" class="outer ng-untouched ng-pristine ng-valid" ng-reflect-form="[object Object]"><ion-item _ngcontent-nld-c140="" class="ion-untouched ion-pristine ion-valid item md ion-activatable ion-focusable item-label hydrated item-interactive item-select item-has-placeholder item-has-value"><ion-label _ngcontent-nld-c140="" class="sc-ion-label-md-h sc-ion-label-md-s md hydrated" id="ion-sel-0-lbl">Type: </ion-label><ion-select _ngcontent-nld-c140="" interface="alert
    " name="typeCode" formcontrolname="typeCode" oktext="OK" canceltext="Dismiss" placeholder="Choose a firm type" ng-reflect-interface="alert
    " ng-reflect-name="typeCode" ng-reflect-ok-text="OK" ng-reflect-cancel-text="Dismiss" ng-reflect-placeholder="Choose a firm type" class="ng-untouched ng-pristine ng-valid ion-untouched ion-pristine ion-valid md in-item hydrated" role="combobox" aria-haspopup="dialog" aria-expanded="false" aria-labelledby="ion-sel-0-lbl"><ion-select-option _ngcontent-nld-c140="" value="EPFIRMI001" ng-reflect-value="EPFIRMI001" role="option" id="ion-selopt-0" class="md hydrated">Informal</ion-select-option><ion-select-option _ngcontent-nld-c140="" value="EPFIRMF001" ng-reflect-value="EPFIRMF001" role="option" id="ion-selopt-1" class="md hydrated">Formal/Ltd.</ion-select-option><input type="hidden" class="aux-input" name="typeCode" value="EPFIRMI001"></ion-select></ion-item><ion-item _ngcontent-nld-c140="" class="ion-untouched ion-pristine ion-valid item md ion-focusable item-label hydrated item-interactive item-input"><ion-label _ngcontent-nld-c140="" class="sc-ion-label-md-h sc-ion-label-md-s md hydrated" id="ion-input-1-lbl">Company name:</ion-label><ion-input _ngcontent-nld-c140="" formcontrolname="name" ng-reflect-name="name" class="ng-untouched ng-pristine ng-valid ion-untouched ion-pristine ion-valid sc-ion-input-md-h sc-ion-input-md-s md hydrated"><input class="native-input sc-ion-input-md" aria-labelledby="ion-input-1-lbl" autocapitalize="off" autocomplete="off" autocorrect="off" name="ion-input-1" placeholder="" type="text"></ion-input></ion-item><ion-item _ngcontent-nld-c140="" class="ion-untouched ion-pristine ion-valid item md ion-focusable item-label hydrated item-interactive item-input"><ion-label _ngcontent-nld-c140="" class="sc-ion-label-md-h sc-ion-label-md-s md hydrated" id="ion-input-2-lbl">Tax ID (NUIT):</ion-label><ion-input _ngcontent-nld-c140="" formcontrolname="taxId" ng-reflect-name="taxId" class="ng-untouched ng-pristine ng-valid ion-untouched ion-pristine ion-valid sc-ion-input-md-h sc-ion-input-md-s md hydrated"><input class="native-input sc-ion-input-md" aria-labelledby="ion-input-2-lbl" autocapitalize="off" autocomplete="off" autocorrect="off" name="ion-input-2" placeholder="" type="text"></ion-input></ion-item><ion-fab-button _ngcontent-nld-c140="" class="md ion-activatable ion-focusable hydrated"><ion-icon _ngcontent-nld-c140="" name="checkmark-outline" ng-reflect-name="checkmark-outline" role="img" class="md hydrated" aria-label="checkmark outline"></ion-icon></ion-fab-button></form></ion-card-content>

EDIT 02/05/2021
The solution below from @VégerLóránd works, however note that when I have finally had a use case which allowed me to test it, my stack had upgraded to the following:

Versions:

  • Angular: 11.2.6
  • Browser: Chrome 90.0.4430.93
  • Cypress Version: 6.7.1
  • Ionic version: 5.29.0
  • O/S: LinuxMint 20

I tested the below solution for interface="alert". I also modified it as below for interface="action-sheet"and this worked too.

        cy.get('[cy-data=type] > ion-select').should((e) =>
        {
            const [dom] = e.get();
            dom.shadowRoot.querySelector('button').click();
        });
        cy.contains('.action-sheet-button', 'Your location name').click();
        //no need to click 'OK' when interface="action-sheet"

Solution

This is how I resolved it, maybe it can help to you as well.
I wrapped ion-select with an ion-item element and added cy-data=’location’ attribute to it.

<ion-item class="ion-no-padding ion-margin-horizontal" cy-data="location">
  <ion-label color="primary">{{ 'location' | translate }}</ion-label>
  <ion-select [(ngModel)]="location" [disabled]="locationDisabled" >
    <ion-select-option *ngFor="let location of locations" value="{{location._id}}">{{location.name}}</ion-select-option>
  </ion-select>
</ion-item>

In the test file this is how you can access to the select:

cy.get('[cy-data=location] > ion-select').should((e) => {
  const [dom] = e.get();
  dom.shadowRoot.querySelector('button').click();
});
cy.contains('.alert-radio-label', 'Your location name').click();
cy.contains('OK').click();

In my case after i clicked the button in the shadowRoot (so the select drop down list itself) the select list appears with location names and with a button what contains OK. So if you have different scenario you need to change these details.

Answered By – Véger Lóránd

Answer Checked By – Robin (Easybugfix Admin)

Leave a Reply

(*) Required, Your email will not be published