Code Monkey home page Code Monkey logo

Comments (25)

csantero avatar csantero commented on May 22, 2024

@Downie I use ember-cli-page-object in my component integration tests. I have to boot up the entire app just as if it were an acceptance test. This is sadly a lot of added boilerplate for each test module, but it does work.

Either ember-cli-page-object needs to be changed to work without using the injected test helpers, or the test helpers need to be available in component integration tests. I feel like the latter is more beneficial in the long run, but when I last looked into it, the test helpers seemed pretty thoroughly intertwined with the app infrastructure.

from ember-cli-page-object.

bedinotti avatar bedinotti commented on May 22, 2024

Okay. I'll spin on this for a bit. I'm hopeful I can figure out a way to do this without having to launch the entire app for a unit test.

@csantero Do you know if there are other hidden dependencies on injected test methods that I might need to contend with?

from ember-cli-page-object.

juanazam avatar juanazam commented on May 22, 2024

@Downie @csantero Hi, thanks for opening this discussion, we were not aware there might be some developers trying to use the addon for unit testing. When we created it, we saw it as an acceptance testing tool, and that's why it relies so heavily on integrating testing functionality.

I think it would be great to provide support for this kind of usage, we need to think how we can include that behavior. In the meantime, @csantero's workaround works, I guess step 1 would be to avoid writing that boilerplate on every unit test and include it in a clever way within the addon.

Thoughts?

from ember-cli-page-object.

csantero avatar csantero commented on May 22, 2024

@Downie Are you talking about component Unit tests or Integration tests? I only use the new-style component integration tests now, so I can't speak for how things differ in unit-land. Here's an example of what an integration test might look like using ember-cli-page-object:

///tests/integration/components/my-component-test.js
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import startApp from 'dummy/tests/helpers/start-app';
import PO from 'dummy/tests/page-object';
import myComponent from 'dummy/tests/pages/helpers/my-component';

let App;

moduleForComponent('my-component', 'Integration | Component | my component', {
  integration: true,
  beforeEach() {
    App = startApp();
  },
  afterEach() {
    Ember.run(App, 'destroy');
  }
});

const page = PO.build({
  myComponent : myComponent()
});

test('it renders', function (assert) {
  this.render(hbs`{{my-component foo="bar"}}`);
  const mc = page.myComponent();
  assert.equal(mc.fooText(), 'bar');
});
///tests/pages/helpers/my-component.js
import PageObject from '../../page-object';

const {
  customHelper,
  text
} = PageObject;

export default customHelper(function () {
  return {
    fooText: text('div.foo')
  };
});

from ember-cli-page-object.

csantero avatar csantero commented on May 22, 2024

@juanazam I love using ember-cli-page-object for component integration tests. I think it's a really good fit. The best is when I can use the same helpers in both integration tests for my ember components and acceptance tests that use those components.

from ember-cli-page-object.

juanazam avatar juanazam commented on May 22, 2024

@csantero That code snippet you provided is very insightful, I guess a good first step would be to implement a blueprint so developers can generate their own integration tests easier. Then we can think of moving forward and avoid that boilerplate.

Would that make sense?

from ember-cli-page-object.

bedinotti avatar bedinotti commented on May 22, 2024

@csantero Ah, good point. Yes, we're still using the old-style unit component test, although this may be another good reason for converting over.

from ember-cli-page-object.

csantero avatar csantero commented on May 22, 2024

@juanazam I agree. We could package a component-test blueprint that overrides the default one from ember-cli. It would yield boilerplate in the test file similar to what I have in my snippet, as well as creating a page-object helper corresponding to the component.

One thing to bear in mind is that when doing this inside an addon, the helper should probably be generated under test-support instead of tests. So if I make component A in my addon, then use A from within component B which is defined in an app that uses that addon, I may want to reference the page object helper for A in the tests for B. In order to do that, the helper must be placed in test-support.

from ember-cli-page-object.

csantero avatar csantero commented on May 22, 2024

Here is an issue on ember-test-helpers tracking being able to use test helpers from component integration tests.

from ember-cli-page-object.

san650 avatar san650 commented on May 22, 2024

I agree that having this addon working on component integration tests would be a great feature.

I'm going to keep a close eye to the github issue that @csantero mentioned.

I didn't had the time to investigate how component integration tests are working right now. Are components rendered on the DOM when you don't instantiate an application? If so, it wouldn't be that hard to add support, we could have an alternate implementation for find and findWithAssert.

On the other hand, having this addon working on unit tests doesn't seems to be easy at all. Also, I'm not sure that's a good idea because in unit tests you usually try to avoid interacting with the DOM.

from ember-cli-page-object.

csantero avatar csantero commented on May 22, 2024

@san650 In my example above, the component gets rendered to DOM inside #ember-testing when you call this.render();

from ember-cli-page-object.

jeradg avatar jeradg commented on May 22, 2024

@san650: In component integration tests (as of Ember 2.2.x), the app is not started during the tests at all (unless you manually add a call to startApp()). After you call this.render(...), the DOM is available via this.$().

I don't think it's a good idea to require the global test helpers to be available in integration tests, nor to require that the app be started. Both options go against typical Ember practices. And starting the app would slow down the tests without adding a real benefit.

While you don't have access to convenience helpers like find() and click() in integration tests, you have the same functionality with the usual jQuery methods this.$('#my-component'), this.$('#my-component button').click(), etc.

I think the trick will be passing the test's this context to the page object. Working from @csantero's example:

///tests/integration/components/my-component-test.js
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import PO from 'dummy/tests/page-object';
import myComponent from 'dummy/tests/pages/helpers/my-component';

// Create the page object as usual. No magic here...
const page = PO.create({
  myComponent: {
    scope: '#my-component',
    submit: PO.clickable('button'),
    fooText: PO.text('.foo')
  }
});

moduleForComponent('my-component', 'Integration | Component | my component', {
  integration: true,

  beforeEach: function() {
    // Pass `this` to the page object, which tells actions, queries, etc.,
    // to use `this.$()` rather than the global test helpers. (The `this` in
    // `beforeEach()` and `afterEach()` is the same one as in the `test()` block
    // that follows.)
    page.setContext(this);
  },

  afterEach: function() {
    // Clear the context.
    // A falsy value passed to `setContext()` results in the global test
    // helpers being used.
    page.setContext(null);
  }
});

test('it submits', function (assert) {
  this.render(hbs`{{my-component id="my-component" foo="bar"}}`);

  // Use the bound page object the same as always.
  page
    .myComponent()
    .submit();

  assert.equal(page.myComponent().fooText(), 'bar');
});

In this example, the only extra work for the user is to add the beforeEach() and afterEach() hooks in order to bind the actions, queries, etc., to this. The page object is declared in the exact same way as usual.

Inside ember-cli-page-object, clickable(), text(), etc., will use the context passed to setContext() if one exists, or the global test helpers otherwise.

If this approach makes sense, I'm happy to work on the feature and submit a pull request.

from ember-cli-page-object.

jeradg avatar jeradg commented on May 22, 2024

Taking the idea one step further, I don't think there's a reason we couldn't add a feature to render the template from within a page object method. So for example:

///tests/integration/components/my-component-test.js
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import PO from 'dummy/tests/page-object';
import myComponent from 'dummy/tests/pages/helpers/my-component';

const page = PO.create({
  // We add a new helper, `render()`, which calls `this.render(hbs`...`)`.
  // Takes one argument, a template string to pass to `hbs`.
  renderWithFoo: PO.render(`{{my-component id="my-component" foo="bar"}}`),
  myComponent: {
    scope: '#my-component',
    submit: PO.clickable('button'),
    fooText: PO.text('.foo')
  }
});

moduleForComponent('my-component', 'Integration | Component | my component', {
  integration: true,

  beforeEach: function() {
    // Pass `this` to the page object, which tells actions, queries, etc.,
    // to use `this.$()` rather than the global test helpers. (The `this` in
    // `beforeEach()` and `afterEach()` is the same one as in the `test()` block
    // that follows.)
    page.setContext(this);
  },

  afterEach: function() {
    // Clear the context.
    // A falsy value passed to `setContext()` results in the global test
    // helpers being used.
    page.setContext(null);
  }
});

test('it submits', function (assert) {
  page
    .renderWithFoo()
    .myComponent()
    .submit();

  assert.equal(page.myComponent().fooText(), 'bar');
});

This is a bit more ambitious than the original suggestion. But once the page object has access to this, I don't see a reason why it wouldn't work.

from ember-cli-page-object.

jeradg avatar jeradg commented on May 22, 2024

Also: The above assumes that only one test at a time is using the PageObject. If multiple tests are using it at once (I don't think Ember qunit currently works like this, but there might be some race condition/async situation I'm not thinking of), calling setContext() multiple times on the one page wouldn't be a good idea.

What we would need is for the current const page = PageObject.create(...) declaration to instead be const MyPage = PageObject.extend(...). Then you would call let page = MyPage.create().setContext(this) for each test. I'm not sure how necessary that actually is, but it would be in keeping with Ember's Object.extend().create() convention.

from ember-cli-page-object.

csantero avatar csantero commented on May 22, 2024

@jeradg This looks pretty neat. One concern I have, and this may just be driven by my own ignorance, but is the behavior of the global click('#my-component') test helper (and others like it) exactly the same as doing this.$('#my-compnent').click()? I was under the impression that the global helpers spawn a run loop and fire the event inside of it.

from ember-cli-page-object.

jeradg avatar jeradg commented on May 22, 2024

@csantero There are some differences. For example, this.$('#my-component').click() doesn't throw an exception if #my-component doesn't exist.

I think you only need to wrap the call to this.$(...).click() in a run loop if it has async side-effects that you're testing for. That depends on the particular component.

Since we don't know if a user's component has actions that cause async side effects, we can wrap calls to this.$(...).click() (or whatever) in Ember.run() within the PageObject action. (Ember.run() doesn't require an app to be running.) We can also manually throw an exception in the click action if this.$(...).length === 0, since that's the expected behaviour from the global helper. (I personally find it annoying when this.(...).click() silently does nothing because the element doesn't exist.)

from ember-cli-page-object.

jeradg avatar jeradg commented on May 22, 2024

@san650 Any thoughts on this? I would love to get to work on a PR if this is a good approach for page objects in component integration tests.

from ember-cli-page-object.

san650 avatar san650 commented on May 22, 2024

@jeradg I really like the ideas and I think we could split the work in multiple steps. The first one is to allow page objects to receive a context object and use that if it's available. I think this is the tricky part.

Note that we're not using the standard Ember object model and the current implementation of PageObject doesn't handle state, the page object it's stateless by design. The original idea was that a single instance of the page object could be used in many tests without the need of reseting it or being recreated.

That being said, I think supporting component integration tests is really important so we should change our approach on how we build the page objects.

We could change the API to have something like this:

import PageObject from '../page-object';

const Page = PageObject.extend({
  ...
});

export default Page;
export const page = Page.create();

Acceptance tests:

import { page } from '../pages/users';

test('...', function(assert) {
  page.foo ...
});

So, this way you could still import just an instance of the page object from the acceptance tests.

Component integration tests:

import Page from '../pages/users';

test('...', function(assert) {
  var page = Page.create(this); // or something similar

  ...
});

@Downie, @csantero, @jeradg What do you think of this approach?

This change it's not trivial and it has to be done for the 1.0 release so we can break the API. Also note that we're reimplementing how we build the pages objects internally to make them more flexible. You can see this work here #92

So the steps would be:

  • For the 1.0
    • Make page objects inheritables by using a extend function
    • Make page objects instantiables by using a create function
  • After 1.0
    • Support test context as a parameter of create
    • Use test context if available in find and findWithAssert helpers
    • Use test context if available in actions helpers

If everyone agrees with this approach I'll start working on the extend and create functions making it easy for PageObjects to have access to state. And it would be great if @jeradg (or anyone else) can work on adding support for component integrations tests. I expect to have the 1.0 version done in about two weeks -- I don't have so much time to dedicate to this project as I would like :-(

Please, let me know your thoughts

from ember-cli-page-object.

san650 avatar san650 commented on May 22, 2024

@jeradg about the render helper, this can be added later because it doesn't break the API. After supporting component integration tests we can start playing with this kind of ideas and see what works and what doesn't 😸

from ember-cli-page-object.

csantero avatar csantero commented on May 22, 2024

@san650 👍

from ember-cli-page-object.

jeradg avatar jeradg commented on May 22, 2024

@san650 That approach sounds good to me.

So the complete to-do list from this issue:

For 1.0:

  • Make page objects inheritables by using an extend function
  • Make page objects instantiables by using a create function

After 1.0:

  • Support test context as a parameter of create
  • Use test context if available in find and findWithAssert helpers
  • Use test context if available in action helpers
  • Add a render helper to call context.render(hbs...) (in component tests)

I'm happy to take a first stab at the first three items in the "After 1.0" list.

@san650 @csantero I have a couple of thoughts on the best way to approach passing this to Page.create() without making it a breaking change. I'll create a separate issue for the "component test context" feature.

from ember-cli-page-object.

jeradg avatar jeradg commented on May 22, 2024

@csantero @Downie: Your dreams have come true. (At least when it comes to using page objects in component tests!)

ember-cli-page-object v1.1.0 introduces support for using your existing page objects in component integration tests.

See the v1.1.0 release notes and the new quickstart guide to get going.

from ember-cli-page-object.

csantero avatar csantero commented on May 22, 2024

@jeradg Very exciting! Thanks for all your hard work on this - I know this is going to make testing components a lot easier for me.

from ember-cli-page-object.

bedinotti avatar bedinotti commented on May 22, 2024

🎉 Excellent! Thanks @jeradg!

from ember-cli-page-object.

jeradg avatar jeradg commented on May 22, 2024

@csantero @Downie Looking forward to your feedback! 😄

from ember-cli-page-object.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.