-
Couldn't load subscription status.
- Fork 92
Description
Summary
Provide a way for the user to specify, and customize integration with a certain test environment via exposing adapters as a public API.
Motivation
Implicitness is not always possible
Today ember-cli-page-object supports several test environments, which can be enabled via different APIs:
moduleForAcceptance(, gets enabled if there iswindow.visitavailable as a global functionmoduleForComponent(, can be enabled bysetContext, and disabled byremoveContexton a page object instance.native-dom-helpers, in addition to the rules above, you have to calluseNativeEvents(to enable this modeRFC268- auto-enabled if neither of the conditions above match, and proper version of@ember/test-helpersis installed
The way we currently support multi-context feature complicates ec-page-object internaly, and adds unnecessary mental load to users, so they need to understand how they can enable or disable some mode.
An opportunity to get rid of old test mode gracefully
Today moduleFor is deprecated in recent versions of ember-qunit. Same for "ember-native-dom-helper".
We basically have 2 ways to approach that:
- just drop "moduleFor" and "native" modes in v2, maybe with deprecations in scope of v1 (so booooring)
- be kind enough, to provide user with a way to implement their own slim integration for
moduleFor, if they still need it for some reason 🎉
Open the door to experimentation
Adapters would also allow the user to create new integrations with alternative test environments.
An immediate example of such integrations can be FastBoot, which already has few different test helpers addons.
Any of that addon can have its own page object adapter.
Detailed design
In order to switch test env, you have to explicitly set an adapter:
import { setAdapter } from 'ember-cli-page-object/adapters';
import ModuleForAcceptanceAdapter from '<some-user-path>/module-for-acceptance';
setAdapter(ModuleForAcceptance);It eliminates the need for all the implicit decisions on the ember-cli-page-object side which we currently do.
A proposed Adapter interface is the following:
interface Adapter {
/**
* Test container Element.
*
* In Ember it's usually "#ember-testing"
*/
get testContainer(): string|Element
visit(path: string): Promise<unknown>
click(element: Element): Promise<unknown>
fillIn(element: Element, content: unknown): Promise<unknown>
triggerEvent(element: Element, eventName: string, eventOptions: TriggerOptions): Promise<unknown>
focus(element: Element): Promise<unknown>
blur(element: Element): Promise<unknown>
}A noticeable difference to ExecutionContexts is that Adapter is no longer responsible for the Element search logic,
which requires an action to find the element before passing it to adapter.
This would clean up the boundries between Actions and Adapters, and simplify internals and testing a lot.
In case user wants their own Adapter or customize existing behavior, they can extend from a built-in adapter and customize it.
For example, if you want FastBoot integration you should be able to do smth like:
import { setAdapter } from 'ember-cli-page-object/adapters';
import Adapter from 'ember-cli-page-object/adapters/rfc-268';
import { visit } from 'ember-cli-fastboot-testing/test-support';
class FastBootTestingAdapter extends Adapter {
visit(path: string) {
return visit(path);
}
}
setAdapter(FastBootTestingAdapter);Caution: snippet above is just for demonstration purpose. In order to make it work we still have to figure out jQuery + NodeJS issue.
Control flow
RFC268 mode would be a default one, so you don't need any special configuration to make it work.
However, if you need to switch, you should define a globaly default test mode in Qunit.testStart:
import Qunit from 'qunit';
import { setAdapter } from 'ember-cli-page-object/adapters';
import DefaultAdapter from 'ember-cli-page-object/adapters/rfc-268';
Qunit.testStart(function() {
// if no other Adapter specified, this one would be used for each test
setAdapter(DefaultAdapter);
});and specify your special adapter per each specific test:
import { module } from 'qunit';
import { setAdapter } from 'ember-cli-page-object/adapters';
import SpecialAdapter from 'my-app/tests/helpers/my-special-adapter';
module('my special module', function(hooks) {
hooks.beforeEach(function() {
// this Adapter would be set only for this specific module
setAdapter(SpecialAdapter);
})
})How we teach this
We can extract "Extend page objects" from the "Getting Started" section, and put it as a self contained section. One of the new "Extending" section sub-nav should be "Adapters". It would explain how to:
- write your own adapter
setAdapterusage
Also we can start providing a deprecation for old execution contexts, using moduleFor and encourage user to:
- switch to RFC268
- or write they own adapter
I expect RFC268 ExecutionContext should be convertable into Adapter seamlessly w/o any breaking changes, so it should not cause any churn for the users who already switch to RFC268.
Drawbacks
Some of the things would become impossible with the adapters.
No more andThen
For example, andThen'like behaviour would not be supported, so there would not be a way to make 2 sync page object action invocations to wait for each other:
page.click();
// invoked immediatelly
page.click();So this part of moduleFor behaviour would not be possible to support via Adapters.
Action can handle exactly 1 Element
Due to the current ExecutionContext implementation, like
ember-cli-page-object/addon-test-support/-private/execution_context/integration.js
Lines 39 to 41 in 873e1cd
| click(selector, container) { | |
| this.$(selector, container).click(); | |
| }, |
it's theoretically possible to invoke an action on multiple elements. This behaviour would not be supported as well, since an action must pass a single Element to adapter.
Alternatives
We can drop ExecutionContexts and just use RFC268 as a single possible mode. However, it closes a window of opportunities to integrate with alternative environments for us.