ember-cli-page-object icon indicating copy to clipboard operation
ember-cli-page-object copied to clipboard

Ability to send keypress events to component element

Open kriswill opened this issue 8 years ago • 2 comments

Hi, I have the need to drive an ember-power-select component during acceptance tests, to simulate it's fuzzy searching capabilities. Using the triggerable property is very cumbersome, and it's input element doesn't respond to simple fillable value changes.

Ideally, I would like to do something like this:

modal: {
    scope: '[data-test-modal]',
    name: fillable('[data-test-name] input'),
    clickAddEmails: clickable('[data-test-add-email]'),
    addEmailInput: keypress( '.ember-power-select-trigger-multiple-input'),
    emails: collection({
        itemScope: '.ember-power-select-option',
        item: {
            email: text(),
            click: clickable()
        }
    }),
    cancel: clickable('[data-test-cancel]'),
    submit: clickable('[data-test-submit]')
},

from the acceptance test:

it('can add an entry, with emails', () => {
    page.visit();
    andThen(() => {
        page.clickAddEntry();
    });
    const modal = page.modal;
    modal.name('Acceptance Test Entry');
    modal.clickAddEmails();
    andThen(() => {
        modal.addEmailInput('@gmail');
        const email1 = modal.emails(0);
    })
});

using the keypress property would send keypress events to the element.

I could always hack around it with jQuery, but a declarative property on the page object would be a lot cleaner.

P.S., I tried making my own property function using extend, but had trouble using getCurrentContext, which is private.

kriswill avatar May 19 '17 19:05 kriswill

ember-power-select provides its own test helpers that make interacting with it possible from page-objects. Here is my current power-select page:

import { clickable, text, attribute} from 'ember-cli-page-object';
import { buildSelector } from 'ember-cli-page-object/extend';
import { assign } from '@ember/polyfills';
import { getExecutionContext } from 'ember-cli-page-object/test-support/-private/execution_context';
import { typeInSearch, clickTrigger as _clickTrigger, findContains as _findContains, selectChoose as _selectChoose } from 'ember-power-select/test-support/helpers';
import { assert } from '@ember/debug';

export default function (scope) {
    return {
        scope: scope || '.ui-filter-multi-select',

        select: selectChoose(),
        clickTrigger: clickTrigger(),
        find: findContains(),
        typeInSearch(text) {
            typeInSearch(text);
            return this;
        },
        placeholder: text('.ember-power-select-placeholder'),
        selected: text('.ember-power-select-selected-item'),
        clear: clickable('.ember-power-select-clear-btn'),
        currentSearchValue: currentSearchValue(),
        isOpen: attribute('aria-expanded', '.ember-power-select-trigger')
    };
}

export var clickTrigger = function (selector, userOptions = {}) {
    return {
        isDescriptor: true,

        get(key) {
            return function () {
                let executionContext = getExecutionContext(this);
                let options = assign({ pageObjectKey: `${key}()` }, userOptions);

                return executionContext.runAsync(() => {
                    let fullSelector = buildSelector(this, selector, options);
                    return _clickTrigger(fullSelector);
                });
            };

        }
    }
};
export var selectChoose = function (selector, userOptions = {}) {
    return {
        isDescriptor: true,

        get(key) {
            return function(value){
                let executionContext = getExecutionContext(this);
                let options = assign({ pageObjectKey: `${key}()` }, userOptions);

                return executionContext.runAsync(() => {
                    let fullSelector = buildSelector(this, selector, options);
                    return _selectChoose(fullSelector, value);
                });       
            }
        }
    };
};

export var findContains = function(selector, userOptions = {}) {
    return {
        isDescriptor: true,

        get(key) {
            return function(text){
                let executionContext = getExecutionContext(this);
                let options = assign({ pageObjectKey: `${key}()` }, userOptions);

                return executionContext.runAsync(() => {
                    let fullSelector = buildSelector(this, selector, options);
                    return _findContains(fullSelector, text);
                });
            }
        }
    };
};

export var currentSearchValue = function() {
    return {
        isDescriptor: true,

        get() {
            // TODO: get this merged into ember-power-select
            let selectors = [
                '.ember-power-select-search-input',
                '.ember-power-select-search input',
                '.ember-power-select-trigger-multiple-input',
                'input[type="search"]'
            ].map((selector) => `${selector}`).join(', ');
            let searchInput = document.querySelector(selectors);
            assert("power-select search exists", searchInput);
            return searchInput.value;
        }
    };
};

A test that required selecting an item would look like

await page.clickTrigger()
    .typeInSearch("Foo")
    .select("Foo")

This works both for acceptance and integration tests.

@ro0gr is there anyway to have done this without relying on the private API for getExecutionContext?

mistahenry avatar Jan 21 '19 13:01 mistahenry

I think integrating of the new @ember/test-helpers typeIn helper should resolve the issue.

@mistahenry there is no public way to define a full featured custom action yet. You can keep using public APIs only, but w/o chaining support at least.

I'm currently working on decoupling of actions from the execution context, which, I think, would make us closer to the custom actions goal.

ro0gr avatar Jan 21 '19 19:01 ro0gr