Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    emulating devices with cypress

    SmartClient Version: SNAPSHOT_v13.1d_2024-09-05/AllModules Development Only (built 2024-09-05)

    Hello, I'm trying to emulate different devices with cypress. Despite its limitations, I think it could be useful for finding issues—at least major ones related to viewport size, like when an element is not visible.

    To emulate different devices, I'm using this code in my test:

    Code:
     const deviceOrientation = 'portrait';
        // const deviceOrientation = 'landscape';
        const device = 'ipad-mini';
        // const device = 'ipad-air';
        // const device = 'desktop';
        const DEVICE_CONFIGS = {
            'ipad-mini': {
                userAgent: 'Mozilla/5.0 (iPad; CPU OS 9_3_5 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13G36 Safari/601.1',
                width: 768,
                height: 1024,
                visitUrl: '/Jat'
            },
            'ipad-air': {
                userAgent: 'Mozilla/5.0 (iPad; CPU OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15A5341f Safari/604.1',
                width: 820,
                height: 1180,
                visitUrl: '/Jat'
            }
        };
        let setUserAgentAndViewport = function (config, orientation) {
            const {width, height} = config;
            if (orientation === 'landscape') {
                cy.viewport(height, width);
            } else {
                cy.viewport(width, height);
            }
            cy.visit(config.visitUrl, {
                onBeforeLoad: (win) => {
                    Object.defineProperty(win.navigator, 'userAgent', {
                        value: config.userAgent,
                    });
                }
            });
            cy.window().then((win) => {
                win.dispatchEvent(new Event('resize'));
            });
        };
    
    ...
    setUserAgentAndViewport(DEVICE_CONFIGS[device], deviceOrientation);
    It works fine with isc.Browser.isTablet, but unfortunately, isc.Page.getOrientation() always returns 'landscape'.

    Is this something that can be fixed in your Cypress integration, or am I out of luck in this scenario?

    #2
    SmartClient Version: SNAPSHOT_v13.1d_2024-09-06/Enterprise Development Only (built 2024-09-06)

    Hello, I'm encountering another issue while testing a ComboBox on a mobile device. I’ve created a test case, which I’m running in the showcase (
    comboboxMobileSample) using the SDK on my MacOS.

    This is the cypress test:

    Code:
    describe('test case', () => {
        let deviceOrientation = 'portrait';
        const device = 'ipad-mini';
        const DEVICE_CONFIGS = {
            'ipad-mini': {
                userAgent: 'Mozilla/5.0 (iPad; CPU OS 9_3_5 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13G36 Safari/601.1',
                width: 768,
                height: 1024,
                visitUrl: 'http://localhost:8080/showcase/?id=comboboxMobileSample&skin=Shiva&fontIncrease=3&sizeIncrease=10'
            }
        };
        let setUserAgentAndViewport = function (config, orientation) {
            const {width, height} = config;
            if (orientation === 'landscape') {
                cy.viewport(height, width);
            } else {
                cy.viewport(width, height);
            }
            cy.visit(config.visitUrl, {
                onBeforeLoad: (win) => {
                    Object.defineProperty(win.navigator, 'userAgent', {
                        value: config.userAgent,
                    });
                }
            });
            cy.window().then((win) => {
                win.dispatchEvent(new Event('resize'));
            });
        };
        it('local showcase comboboxMobileSample', () => {
            setUserAgentAndViewport(DEVICE_CONFIGS[device], deviceOrientation);
    
            cy.getSC('//testRoot[]/child[Class=DynamicForm||index=0||length=1||classIndex=0||classLength=1]/item[name=filteredCombo||title=Select%20Item||index=0||Class=ComboBoxItem]/[icon="picker"]')
                .click()
    
            cy.getSC('//testRoot[]/child[Class=DynamicForm||index=0||length=1||classIndex=0||classLength=1]/item[name=filteredCombo||title=Select%20Item||index=0||Class=ComboBoxItem]/pickerSearchForm/item[name=s||title=s||index=0||Class=TextItem]/element')
                .type('Adding')
        });
    });
    First, there’s a minor issue: the first selector, retrieved via the AutoTest system by clicking on the text box part of the ComboBox, doesn’t open the pickListMenu. I need to either remove the final '/element' part from the selector or click directly on the picker icon. I believe this step isn’t necessary in the non-mobile version.

    The major issue is with the second locator, where I want to filter the list using the pickerSearchForm. This almost never works—it only succeeds in very rare cases (maybe 1 out of 30 attempts).
    What usually happens is that it starts typing 'Adding' (I’m not sure if it types the whole string), but the problem is that the pickListMenu closes unexpectedly.

    As a result, the actual test, which is supposed to select an option, fails.
    Last edited by claudiobosticco; 6 Sep 2024, 07:53.

    Comment


      #3
      So first, as an obligatory overall note - Cypress "mobile emulation" is extremely crude, similar to the "emulators" built into various browsers, and such "emulators" often end up with the framework deciding that it's dealing with some chimera between two different browsers. (like "isIE" and "isChrome" both being true).

      This is a problem because, despite the philosophical position put forward by JQuery and others that a framework should just "detect" what the current browser's bugs are, this is not actually feasible when the bugs are related to performance, or cause uncatchable errors, or result in rendering errors we can't feasibly detect.

      This means we can't generally accept bugs from false "emulators" that just change the userAgent and such.

      However, we're going to look at both problems here to see if we can do better than what we're currently doing.

      On #2 - can you clarify what's going on: did you:

      1) capture a locator on true mobile, and try to play it back on Cypress emulation

      .. or ..

      2) capture a locator on desktop without Cypress emulation, and try to play it back on mobile

      .. or something else?

      Comment


        #4
        Originally posted by Isomorphic View Post
        So first, as an obligatory overall note - Cypress "mobile emulation" is extremely crude, similar to the "emulators" built into various browsers, and such "emulators" often end up with the framework deciding that it's dealing with some chimera between two different browsers. (like "isIE" and "isChrome" both being true).

        This is a problem because, despite the philosophical position put forward by JQuery and others that a framework should just "detect" what the current browser's bugs are, this is not actually feasible when the bugs are related to performance, or cause uncatchable errors, or result in rendering errors we can't feasibly detect.

        This means we can't generally accept bugs from false "emulators" that just change the userAgent and such.
        I agree, and I understand I'm walking a very fine line with these 'emulators'
        Originally posted by Isomorphic View Post
        On #2 - can you clarify what's going on: did you:

        1) capture a locator on true mobile, and try to play it back on Cypress emulation

        .. or ..

        2) capture a locator on desktop without Cypress emulation, and try to play it back on mobile

        .. or something else?
        Good question, as I didn’t think it would make a difference. I captured them directly on the emulator run by Cypress. It usually worked. Should I be doing it differently?

        Comment


          #5
          It can make a difference because the same logical element may be in a different DOM hierarchy or even component hierarchy in mobile vs non-mobile. Consider the input field of a comboBox: it's right in the form on desktop, and then for mobile, it appears in a pop-up.

          Our SCLocator system compensates for a lot of things, but there are cases that are inherently hard. Taking again the ComboBox:

          1) if you want to write a test that types into a comboBox, and run it in mobile mode, you probably want to focus in the field to cause the pop-up to appear before you try to type a search string. That's not necessary for desktop

          2) if you are dismissing the comboBox pop-up on mobile, you click the "Cancel" button. That doesn't even exist on desktop - you just click outside to dimiss

          So, even with SCLocators matching perfectly between mobile and desktop, there can still be differences in the event series, what elements are present, gestures etc that could trip up tests that are recorded on mobile but played back on desktop (or vice versa).

          The only way to get around this would be to use a library of high-level logical actions (comboBox_input or comboBox_dismiss). But at that point you have moved pretty far away from browser-level testing, and almost might as well be calling dynamicForm.setItemValue() instead, which is a different style of test.

          So, in light of all of this, we would recommend:

          1) record and playback the vast majority of your tests on desktop. The point of the test suite is to catch regressions in application logic, and that is presumably almost identical across the desktop and mobile versions of your app

          2) if you want some mobile-specific tests - for cases where your application logic is actually different on mobile in some area - then record and playback those tests on mobile

          Comment


            #6
            Thanks for your reply, but when I said that I captured the locator directly on the emulator run by cypress, I meant the emulator configured as in the testcase above (post #2), so I actually captured the locator in the "mobile emulator": in fact, the comboBox is opening the pickListMenu in "mobile" mode, ie with pickListPlacement:"fillScreen", and the pickerSearchForm on top.
            The problem is that it types in the pickerSearchForm but the pickList hides "by itself" without permitting to choose an option.

            Comment


              #7
              We will be checking on being able to drive a comboBox in true mobile mode, but as previously mentioned, Cypress' mobile mode is just browser spoofing, so we don't want to waste effort on that - we would only go in for very trivial workarounds.

              Bear in mind, if you just record and playback on desktop, you will not hit this issue. At the moment, you are writing a test that, even if you worked around the weird Cypress issue here, still wouldn't play back on desktop (no pickerSearchForm), so it doesn't seem like that advances the goal of testing your application logic.

              Comment


                #8
                Hello, about this issue, I just discovered that if I modify the test case like this:

                Code:
                import { slowCypressDown } from 'cypress-slow-down'
                slowCypressDown()
                
                describe('test case', () => {
                    let deviceOrientation = 'portrait';
                    const device = 'ipad-mini';
                    const DEVICE_CONFIGS = {
                        'ipad-mini': {
                            userAgent: 'Mozilla/5.0 (iPad; CPU OS 9_3_5 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13G36 Safari/601.1',
                            width: 768,
                            height: 1024,
                            visitUrl: 'http://localhost:8083/showcase/?id=comboboxMobileSample&skin=Shiva&fontIncrease=3&sizeIncrease=10'
                        }
                    };
                    let setUserAgentAndViewport = function (config, orientation) {
                        const {width, height} = config;
                        if (orientation === 'landscape') {
                            cy.viewport(height, width);
                        } else {
                            cy.viewport(width, height);
                        }
                        cy.visit(config.visitUrl, {
                            onBeforeLoad: (win) => {
                                Object.defineProperty(win.navigator, 'userAgent', {
                                    value: config.userAgent,
                                });
                            }
                        });
                        cy.window().then((win) => {
                            win.dispatchEvent(new Event('resize'));
                        });
                    };
                    it('local showcase comboboxMobileSample', () => {
                        setUserAgentAndViewport(DEVICE_CONFIGS[device], deviceOrientation);
                        // this locator doesn't work.
                        // cy.getSC('//testRoot[]/child[Class=DynamicForm||index=0||length=1||classIndex=0||classLength=1]/item[name=filteredCombo||title=Select%20Item||index=0||Class=ComboBoxItem]/element')
                        // .click()
                         cy.getSC('//testRoot[]/child[Class=DynamicForm||index=0||length=1||classIndex=0||classLength=1]/item[name=filteredCombo||title=Select%20Item||index=0||Class=ComboBoxItem]')
                             .click()
                
                        cy.getSC('//testRoot[]/child[Class=DynamicForm||index=0||length=1||classIndex=0||classLength=1]/item[name=filteredCombo||title=Select%20Item||index=0||Class=ComboBoxItem]/pickerSearchForm/item[name=s||title=s||index=0||Class=TextItem]/element')
                            .click().type('Adding')
                    });
                });
                so that now it clicks before typing in the pickerSearchForm, but I also use slowCypressDown (https://github.com/bahmutov/cypress-slow-down) then it works.

                Have you got any idea of what's possibly going on?

                Comment


                  #9
                  even a delay of one millisecond is sufficient, ie
                  Code:
                  slowCypressDown(1)

                  Comment

                  Working...
                  X