Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate ember-native-dom-helpers #325

Closed
wants to merge 20 commits into from

Conversation

ro0gr
Copy link
Collaborator

@ro0gr ro0gr commented Aug 20, 2017

addresses #311
reviewers @san650 @simonihmig

ember-native-dom context allows not to rely on global ember test helpers(click, fillIn,..).
We still use jquery due to custom selectors capabilities(:is, :contains etc)

In order to allow ember application not to use jquery while keeping a backward compatibility ember-cli-page-object now bundles its own jquery copy namespaced as '-jquery'.

To enable native dom helpers in your tests, put this lines into your tests/test-helper.js:

// tests/test-helper.js
...
import { useNativeDOMHelpers } from 'ember-cli-page-object/extend';

useNativeDOMHelpers(); // this will replace classic contexts with `e-n-d-h`

Tests

I've expanded property tests with [native-dom] mode for both integration and acceptance contexts. It seems a little not optimal however it gives a solid code coverage with a minimal efforts.

Todo

} else {
let testsContainer = this.testContext ?
this.testContext._element :
'#ember-testing';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely not the best thing to be hard-coded

guardMultiple(result, selector, options.multiple);

return result;
},

$(selector, container) {
Copy link
Collaborator Author

@ro0gr ro0gr Aug 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ro0gr
Copy link
Collaborator Author

ro0gr commented Aug 20, 2017

Actually fillIn helper is not used currently due to support of [contenteditable]

@ro0gr
Copy link
Collaborator Author

ro0gr commented Aug 20, 2017

Seems like e-n-d-h can absorb most of differences between acceptance and integration contexts.

Copy link
Contributor

@simonihmig simonihmig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ro0gr, great that you have started working on this. I have not so many comments about the implementation details, mainly because I am not that familiar with the code base. But I will write some other thoughts down later...

triggerEvent(selector, container, 'input');
triggerEvent(selector, container, 'change');
triggerEvent($selection, 'input');
triggerEvent($selection, 'change');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we simply use the built-in fillIn helper of e-n-d-h here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, just saw your other comment, so ignore this...

index.js Outdated
@@ -11,6 +11,9 @@ module.exports = {
enabled: this._shouldIncludeFiles(),
import: ['index.js']
};
},
jquery: {
import: ['dist/jquery.js'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens here when your app has not opted out of using jQuery? Do we then bundle jQuery twice?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be verified.
Anyways seems like it's not a big deal cause it's for testing only.
Also this way we can guarantee that we don't consume jquery from ember

Copy link
Collaborator Author

@ro0gr ro0gr Sep 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just used this branch on a project with jquery@1.11.3 included. As a result both jquery versions are bundled into vendor.js.

The interesting thing is that 1.11.3 is still used by ember and v3.2.1 by e-c-p-o so their are no in conflict.

upd: tested in addon with ember originated from bower

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all the jquery plugins should be included before e-c-p-o. Otherwise they will extend e-c-p-o's jquery instead of Ember.$ :/

@simonihmig
Copy link
Contributor

IIUC you have jQuery still around as a compatibility layer for jQuery-specific selectors like :eq(), right? I cannot remember that I have ever used this in tests. So what about the following other approach:

  • we keep the existing execution contexts around basically unchanged
  • we add additional ones (maybe even unified for acceptance and integration?) based on e-n-d-h, that do not require jQuery at all. Obviously non-standard selectors will not work with them. Users have a way to opt in into the new native execution contexts, e.g. by setting some option in ember-cli-build.js, so some broccoli funnel replaces the default context files with those new native-based ones.

This way

  • we could keep the existing ones untouched, reducing the risk to break something
  • the implementation of the new native contexts could focus on a native-only implementation, making this easier to reason about. And they would not need to be backwards compatible!
  • these new ones could land with some experimental support, for users to try this out
  • once we are happy with them, we can declare them stable, and add some deprecation message to the old ones
  • with a major version bump the old ones could be dropped and the new ones become the new standard

What's your thoughts on this? @ro0gr @san650

cc @cibernox interested in chiming in here?

@ro0gr
Copy link
Collaborator Author

ro0gr commented Aug 21, 2017

@simonihmig thank you for your thoughts.

IIUC you have jQuery still around as a compatibility layer for jQuery-specific selectors like :eq(), right?

Yes. I think all the specific selectors can be found in Selector class

we add additional ones (maybe even unified for acceptance and integration?) based on e-n-d-h, that do not require jQuery at all

Unfortunately I think it's impossible right now. Lots of properties rely on buildSelector() which builds selectors with jq like filters. It means that with the native context getting of nth item from collection will not work.

we could keep the existing ones untouched

nah 😋 Currently I'm working on merging contexts. Seems like e-n-d-h absorbs most of the differences between integration/acceptance modes.
I've added 2 native execution contexts co-located with the current ones. You can switch contexts with useNativeDOMHelpers(true|false) right now.
This way we don't touch existing execution contexts.

@coveralls
Copy link

Coverage Status

Coverage decreased (-6.9%) to 90.819% when pulling 5a3106b on ro0gr:native-dom-helpers into 88fad42 on san650:master.

@ro0gr
Copy link
Collaborator Author

ro0gr commented Aug 22, 2017

I agree with @simonihmig that leaving classical contexts untouched is a good thing. So I've reverted all the changes in classic contexts with global ember helpers.

In order to enable e-n-d-h now you can use useNativeDOMHelpers:

// tests/test-helper.js
...
import { useNativeDOMHelpers } from 'ember-cli-page-object/extend';

useNativeDOMHelpers(); // this will replace classic contexts with `e-n-d-h`

Currently it doesn't accept any args.

Users have a way to opt in into the new native execution contexts, e.g. by setting some option in ember-cli-build.js, so some broccoli funnel replaces the default context files with those new native-based ones.

I've thought about build time config for this. But I think this way it will be harder to test all the contexts inside a single e-c-p-o test suite. So I've chosen run-time switch in test-helper.js

@coveralls
Copy link

Coverage Status

Coverage decreased (-6.9%) to 90.819% when pulling 1de4377 on ro0gr:native-dom-helpers into 88fad42 on san650:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-6.9%) to 90.819% when pulling 6e722ee on ro0gr:native-dom-helpers into 88fad42 on san650:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-6.9%) to 90.819% when pulling 7c4982b on ro0gr:native-dom-helpers into 88fad42 on san650:master.

@ro0gr
Copy link
Collaborator Author

ro0gr commented Sep 30, 2017

Let's assume the following:

  • jquery is excluded from the app
  • you have installed this branch of ecpo (before "truly isolate jquery" commit)
  • some sly jquery plugin is left in your code base by accident

As a result ember s and ember t will work because this jquery plugin is able to use jquery from ecpo.
At the same time staging/production builds are getting unusable as long as ecpo is stripped from production builds.

I've ended up in similar situation. truly isolate jquery tries to address this issue by calling .noConflict() and namespacing jquery as a pivate module '-jquery'. This trick is happening right after local jquery version is imported.

beforeEach() {
this.adapter = new AcceptanceAdapter(AcceptanceExecutionContext);
},
[true, false].forEach(_useNativeDOMHelpers => {
Copy link
Collaborator Author

@ro0gr ro0gr Oct 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of this. But it gives us an ability to test native-dom-helpers almost for free. So we can use existing test suite as a baseline.
I have few ideas on how to improve testing story, but I prefer to leave it for a separate issue/pr.

index.js Outdated
@@ -23,6 +30,11 @@ module.exports = {

this.app = app;

if (this._shouldIncludeFiles()) {
this.import('vendor/shims/-jquery.js', { prepend: true });
this.import('vendor/ecpo-jquery/dist/jquery.js', { prepend: true });
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensure shims goes right after our private jquery instance

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we put a comment here indicating that this ordering is important for shims/-jquery to work? The comment in shims/-jquery is really helpful, and I think the assumption we're making there about ordering will be more secure if there is also a warning here, where that ordering is defined.

@@ -0,0 +1,11 @@
(function() {
var jq = self['$'].noConflict();
Copy link
Collaborator Author

@ro0gr ro0gr Oct 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we heavily rely on the assumption that we included jquery right a before this script
https://github.com/san650/ember-cli-page-object/pull/325/files#diff-168726dbe96b3ce427e7fedce31bb0bcR35

this way noConflict gives us our jquery instance and disposes it from the window.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll inline some comments in code a little bit later

@@ -33,7 +33,9 @@ IntegrationExecutionContext.prototype = {
},

// Do nothing in integration test
visit: $.noop,
visit() {
throw new Error('visit is not supported in integration mode');
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should I rollback this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. If someone calls visit in an integration test, they'll get some feedback that will help them learn more about how to use ember-cli-page-object. I wonder...what happens if you set the context in an integration test and then forget to remove it? Is it possible that we would end up seeing this message in an acceptance test due to improper cleanup of an integration test? If so, then the error message should probably address this. Suggest using throwBetterError for consistency with error messaging elsewhere.

Copy link
Collaborator Author

@ro0gr ro0gr Oct 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to use throwBetterError here. Unfortunately it doesn't work out of the box because throwBetterError is currently coupled exlusively to element find failures.

This pr is already quite large. I've reverted original silent behavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference, here's an example of where throwBetterError is used outside the context of element find failures: https://github.com/san650/ember-cli-page-object/blob/master/addon/-private/properties/alias.js#L86.

@ro0gr
Copy link
Collaborator Author

ro0gr commented Oct 5, 2017

Currently all the trigger tests which test keypress 13 are failing on CI in native-dom-helpers mode.
I see similar issue with IE currently. At the same time it works perfectly with chrome and firefox.

I'm sure the issue originated from fireEvent.
The next step is to fix this.


update: cibernox/ember-native-dom-helpers#112

@@ -33,7 +33,9 @@ IntegrationExecutionContext.prototype = {
},

// Do nothing in integration test
visit: $.noop,
visit() {
throw new Error('visit is not supported in integration mode');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. If someone calls visit in an integration test, they'll get some feedback that will help them learn more about how to use ember-cli-page-object. I wonder...what happens if you set the context in an integration test and then forget to remove it? Is it possible that we would end up seeing this message in an acceptance test due to improper cleanup of an integration test? If so, then the error message should probably address this. Suggest using throwBetterError for consistency with error messaging elsewhere.

@@ -0,0 +1,131 @@
import $ from '-jquery';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more appropriate to call this file native-context.js?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I also thought about it.
Actually I like dom-context more. native sounds misleading to me a little bit. Maybe native-dom-context is more appropriate here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

native-dom-context sounds good to me!

throw new Error('not implemented');
},

// Do nothing in integration test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment might be left over from an earlier copy/paste?

return $(selector, container);
} else {
let testsContainer = this.testContext ?
this.testContext._element :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice not to have to use the private _element property, and it looks like there's support in the community for making element part of the public API. Suggest adding a TODO comment with a link to this issue so that we can update this once that issue is handled.

Copy link
Collaborator Author

@ro0gr ro0gr Oct 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#ember-testing one line above also doesn't look very nice.

},

fillIn(selector, container, options, content) {
let el = this.$(selector, container)[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are selecting only the first matched element here, what would happen if someone called fillIn in a situation where { multiple: true } is present? What do we gain from passing an element into fillElement instead of a jQuery selection?

Copy link
Collaborator Author

@ro0gr ro0gr Oct 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never used fillIn with { multiple: true } :)

I'll update it. thanks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to fillable API docs there is no support for multiple option. Does fillable intend to support this option?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that it's not called out in the docs, but fillable does work with { multiple: true }, so it's probably safest to preserve that functionality at this point.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, will do. Thx

addon/extend.js Outdated
import IntegrationNativeContext from './-private/execution_context/integration-native';
import AcceptanceNativeContext from './-private/execution_context/acceptance-native';
import IntegrationClassicContext from './-private/execution_context/integration';
import AcceptanceClassicContext from './-private/execution_context/acceptance';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of contrasting naming like Native vs Classic. Suggest IntegrationJQueryContext and AcceptanceJQueryContext to increase clarity. Might even be good to rename execution_context/integration and execution_context/acceptance to execution-context/integration-jquery and execution-context/acceptance-jquery.

Copy link
Collaborator Author

@ro0gr ro0gr Oct 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest IntegrationJQueryContext and AcceptanceJQueryContext to increase clarity.

Currently all the 4 actual contexts use jquery to find elements.I'd suggest to rename all the contexts. I like your idea to rename existing contexts.

How about?

  • acceptance-ember-context,
  • integration-ember-context
  • acceptance-native-dom-context
  • integration-native-dom-context

It seem too verbose to me. But I'm ok with it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about acceptance-jquery-event-context, acceptance-native-event-context, etc? And then, native-event-context instead for context.js above? If the file names are too long, we could add acceptance and integration folders, each of which could include native-event-context and jquery-event-context.

Copy link
Collaborator Author

@ro0gr ro0gr Oct 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about acceptance-jquery-event-context

  • i'm not sure why do you refer to event, triggering of events is only a part of context
  • jquery is not dedicated only for existing contexts. All of contexts(including native-dom) use jquery to find elements to find DOM elements. More details here

native-event-context instead for context.js above?

yes, sure. Hovewer I think we agreed on native-dom-context name

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that the core difference between these contexts is the way in which the events are triggered, so that's why I'm suggesting filenames with event in them as well as native-event-context instead of native-dom-context. Not a big deal either way...just pondering how to achieve the most clarity.

Copy link
Collaborator Author

@ro0gr ro0gr Oct 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I see your point about events. However acceptance-jquery-event-context is still not explanatory enough cause it also uses global ember test helpers(find, click).
I also agree with you that it's not a big deal cause it's not a part of public API.
I think it might be better not too touch exiting context names at all until a better name eventually comes to us :)

@ro0gr
Copy link
Collaborator Author

ro0gr commented Oct 15, 2017

hm.., I've just checked IE and all the tests started to pass(even in IE9 emulation mode) after keyEvent fix. However CI with phantomjs still fails.

I'm not sure if we want to fix issues in phantomjs because it's abandoned in community now...

@ro0gr ro0gr changed the title [wip] replace ember global test helpers with native dom helpers Integrate ember-native-dom-helpers Oct 15, 2017
@ro0gr
Copy link
Collaborator Author

ro0gr commented Oct 18, 2017

I think the best way to avoid issue with phantomJS is to replace it with another browser #333

let elements = this.$(selector, container).toArray();

elements.forEach((el) => {
fillElement(el, content, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo:

  1. Update function signature of fillElement to indicate that the first param is an element
  2. Update the usage of fillElement in the other places where it is used so that we are always passing an element and not a jQuery selection.

index.js Outdated
@@ -23,6 +30,11 @@ module.exports = {

this.app = app;

if (this._shouldIncludeFiles()) {
this.import('vendor/shims/-jquery.js', { prepend: true });
this.import('vendor/ecpo-jquery/dist/jquery.js', { prepend: true });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we put a comment here indicating that this ordering is important for shims/-jquery to work? The comment in shims/-jquery is really helpful, and I think the assumption we're making there about ordering will be more secure if there is also a warning here, where that ordering is defined.

@pzuraq
Copy link
Contributor

pzuraq commented Oct 31, 2017

I tried this branch locally to see if it would work (we use native dom events so this is going to be necessary for us) and I'm getting define is not a function, appears that the jquery shim file is getting prepended before define is actually assigned to be function. This is with ember-cli@2.14.0

@ro0gr
Copy link
Collaborator Author

ro0gr commented Oct 31, 2017

@pzuraq thank you for report. I've tested it against ember-cli@2.12 and 2.16 and haven't noticed such issue yet.

I'll test it with 2.12. However some more additional details would be helpful:

  • what's you npm version
  • is it happening inside an app or addon
  • do you do npm link or just specify a git URL in your package.json

Thanks

@pzuraq
Copy link
Contributor

pzuraq commented Oct 31, 2017

  • We're using yarn@v1.1.0
  • This is happening inside an addon
  • I'm using a git URL in package.json

};

AcceptanceExecutionContext.prototype.runAsync = function(cb) {
window.wait().then(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be the last dependency on a global test helper, which means we still have to use setContext to be able to use PageObjects in integration tests. We can do the same thing ember-native-dom-helpers does here to make this completely independent:

import wait from 'ember-test-helpers/wait';

(window.wait || wait)().then(() => {});

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right , seems like it should be fixed.

@ro0gr
Copy link
Collaborator Author

ro0gr commented Nov 5, 2017

@pzuraq I've updated handling of jquery per you suggestion in slack.
Now we store it in a temporary global variable in the very beginning and later expose it as an amd module.

@ro0gr ro0gr mentioned this pull request Nov 11, 2017
1 task
@ro0gr
Copy link
Collaborator Author

ro0gr commented Nov 11, 2017

superseded by #339

@ro0gr ro0gr closed this Nov 11, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants