Skip to content

Run on e2e setup, test add to project flow, prepwork for Run On OpenShift tests #1057

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

Merged
merged 1 commit into from
Jan 25, 2017

Conversation

benjaminapetersen
Copy link
Contributor

@benjaminapetersen benjaminapetersen commented Dec 19, 2016

Fixes openshift/origin#6746

This PR is easiest to follow starting with: test/integration/features/user_adds_template_to_project.spec.js.

A few points:

  • test/integration/features is where I moved the actual test cases based on patterns I've seen in other protractor suites.
  • Individual tests are named via flows a user would take through the app
  • The /page-objects directory contains file that understand how to interact with individual pages in the web console
  • The /helpers dir contains some shared helpers
  • The /fixtures dir contains support data

If we can get this in, I will start working on the next step to support testing Run On Openshift, which will involve hacking the whitelist (see helpers/env.js) to install templates to a project that we can then use to test create via url.

RUN this suite alone:
$ grunt test-integration --suite=add-to-project
RUN the original suite that verified project creation:
$ grunt test-integration --suite=create-project
@jwforres @spadgett @rhamilto

@benjaminapetersen benjaminapetersen force-pushed the run-on-e2e-setup branch 2 times, most recently from 104dd2e to 24188ef Compare December 20, 2016 18:09
@benjaminapetersen
Copy link
Contributor Author

rebased

beforeEach(function() {
h.commonSetup();
h.login();
projectHelpers.deleteAllProjects();
Copy link
Member

Choose a reason for hiding this comment

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

I'm unclear why we're deleting all projects before every test. Will this delete everything I've created if I run the tests in my development environment?

It seems like the test that creates the project should delete it on tear down.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should, but if a test fails protractor dies. It doesn't cleanup (and delete the project). So this is needed to ensure you start clean slate every time, otherwise you have to manually login as e2e-user and delete them by hand.

Copy link
Member

Choose a reason for hiding this comment

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

OK, so it only deletes e2e-user projects? I'd add a comment explaining.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do.

Copy link
Member

Choose a reason for hiding this comment

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

dont forget to add the comment here like @spadgett requested

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, I'll add the comment in the projectHelpers file above deleteAllProjects

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, actually I already did there:

// All projects visible to the current user.
// This function will click the 'delete' on every project that appears on the project list page.
// Be careful about using this function if your test gives the e2e-user access
// to internal projects such as openshift, or openshift-infra

var deploymentsPage = new DeploymentsPage(project);
deploymentsPage.visit();
var tableRows = element(by.repeater('replicationController in replicationControllersForDC'));
h.waitForElem(tableRows);
Copy link
Member

Choose a reason for hiding this comment

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

This testing style reads very well.

_merge(target, source);
});
return target;
};
Copy link
Member

Choose a reason for hiding this comment

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

Is there no lodash utility that does this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, both _.merge and _.extend would work. I think we haven't used Lodash in the tests yet, so I didn't require() it. We can start though and drop this helper.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Part of me wants to borrow the API from Backbone.js for this and do:

exports.FooPage = Page.extend({
  // add methods...
});

and eliminate the clunkiness of extend(FooPage.prototype, Page.prototype, { /* */ }), but that can prob come as a cleanup later.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I think I'd rather use _.extend (or even angular.extend) rather than rewriting it. I don't really see a reason not to include lodash in our test code if we need it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  • replace extend with _.extend

if(!selected) {
return checkboxElem.click();
}
});
Copy link
Member

Choose a reason for hiding this comment

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

Most of the codebase uses indentation style:

  return checkboxElem.isSelected().then(function(selected) {
    if(!selected) {
      return checkboxElem.click();
    }
  });

I think we should start using that consistently.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Plug for PR #682 (eslint)! I'll update this.

@@ -61,6 +61,8 @@ exports.deleteAllProjects = function() {
browser.wait(protractor.ExpectedConditions.elementToBeClickable(deleteButton), 2000);
deleteButton.click();
h.waitForPresence(".alert-success", "marked for deletion");
// dont click next until we know the modal disappeared
browser.sleep(1000);
Copy link
Member

Choose a reason for hiding this comment

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

Is it better to have a waitForRemoval or similar and wait for the DOM element to disappear?

Copy link
Contributor Author

@benjaminapetersen benjaminapetersen Dec 21, 2016

Choose a reason for hiding this comment

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

Great idea, I'll add this.

  • create h.waitForRemvoal() helper

var tabs = element(by.css('.nav-tabs'));
h.waitForElem(tabs);
return tabs;
},
Copy link
Member

Choose a reason for hiding this comment

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

Suggest a utility for clicking tabs that we can use for any page. We have a lot of tabs.

Copy link
Contributor Author

@benjaminapetersen benjaminapetersen Dec 21, 2016

Choose a reason for hiding this comment

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

Agree

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm. I'm going to follow-up TODO for this one. I'm not sure if it should be a helper, or something built into the Page itself (like Page menu items) but tabs defined via constructor.

},
clickLogout: function() {
return clickHelpMenuChild(by.id('iser-dropdown'), by.css('.dropdown.open .uib-dropdown-menu'), 0);
}
Copy link
Member

Choose a reason for hiding this comment

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

Can we find the item by the label? Doing it by index will break if we ever add items in the middle or reorder the menu.

Copy link
Contributor Author

@benjaminapetersen benjaminapetersen Dec 21, 2016

Choose a reason for hiding this comment

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

  • find by label or cssContainingText

},
// applications submenu
clickDeployments: function() {
return clickSideMenuChild(by.repeater('primaryItem in navItems'), 1, by.repeater('secondaryItem in secondarySection.items'), 0);
Copy link
Member

Choose a reason for hiding this comment

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

Same comment here: I'd really like to avoid selecting things by child index.

Copy link
Contributor Author

@benjaminapetersen benjaminapetersen Dec 21, 2016

Choose a reason for hiding this comment

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

  • fix selector

@@ -172,10 +174,10 @@ exports.config = {
// The timeout in milliseconds for each script run on the browser. This should
// be longer than the maximum time your application needs to stabilize between
// tasks.
allScriptsTimeout: 11000,
allScriptsTimeout: 30 * 1000,//11000,
Copy link
Member

Choose a reason for hiding this comment

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

So... 30 * 1000 != 11000, remove the comment?

I think // 30 seconds would be a better comment :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ha, oop, i left the old value in the comment as I was tinkering. Will eliminate, I think 30 seconds is obvious here.
Math win..?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I'm fine not adding the 30s comment because it's easy to forget to fix those when the code changes


// How long to wait for a page to load.
getPageTimeout: 10000,
getPageTimeout: 30 * 1000, //10000,
Copy link
Member

Choose a reason for hiding this comment

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

Remove the incorrect comment

@spadgett spadgett self-assigned this Dec 21, 2016
@benjaminapetersen
Copy link
Contributor Author

General Q for @spadgett @jwforres going forward, since this model using PageObjects is emulating classical programming, and we are in Node.js, any concerns over using some ES6 syntax?

// classes
class Page {
  // etc
}

class FooPage extends Page {
  // etc
}

// fat arrows
doFoo.then((etc) => {
 // do things with etc
})

Since we aren't in the browser this might be appropriate. I'm not gonna change it for this PR, but going forward would be good to answer how we want to shape our tests.

@benjaminapetersen benjaminapetersen force-pushed the run-on-e2e-setup branch 2 times, most recently from fe3e19f to d024745 Compare December 21, 2016 20:24
@benjaminapetersen
Copy link
Contributor Author

@spadgett second pass? other than the tabs comment, all should be fixed.

var _ = require('lodash');

exports.startCase = function(str) {
return str && _.startCase(str) || '';
Copy link
Member

Choose a reason for hiding this comment

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

Can we just call _.startCase directly? It handles null / undefined values already (returning '')

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup. I'll delete this.


// example:
// unCheck(element(by.css('input[type="checkbox"]')))
var unCheck = function(checkboxElem) {
Copy link
Member

Choose a reason for hiding this comment

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

nit: can we call it uncheck :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@spadgett
Copy link
Member

[test]

@spadgett
Copy link
Member

1) User adds a template to a project after creating a new project using the "Import YAML/JSON" tab should process and create the objects in the template
  - Error: Not found
Wait timed out after 5085ms

@benjaminapetersen
Copy link
Contributor Author

Interesting, gonna
[TEST]
again while I debug locally. Its struggling to find the 2 rows in the deployments table.
at /data/src/github.com/openshift/origin-web-console/test/integration/features/user_adds_template_to_project.spec.js:43:15
Will be curious to see if its flakey, no trouble finding these when I run on my machine.

@benjaminapetersen
Copy link
Contributor Author

@spadgett hmmm. Curious if you have any thoughts. I'm wondering if there is latency in the test environment? I can run these locally fine. It fails when it gets to the end of the test & checks to see if the deployments are listed.

@benjaminapetersen
Copy link
Contributor Author

Ok, passed once, removing an extra timeout & [TEST] one more time

@benjaminapetersen
Copy link
Contributor Author

[TEST]
jshint lies.

@benjaminapetersen
Copy link
Contributor Author

[TEST] with a shorter sleep hack

@benjaminapetersen
Copy link
Contributor Author

[TEST]

@benjaminapetersen
Copy link
Contributor Author

[TEST]

@benjaminapetersen
Copy link
Contributor Author

@spadgett @jwforres @rhamilto yay, they run!!!!!
Ready for a review. Once these get a 👍 I can write the tests for the run-on stuff.

@benjaminapetersen
Copy link
Contributor Author

[TEST]

@benjaminapetersen
Copy link
Contributor Author

[TEST]

@benjaminapetersen
Copy link
Contributor Author

@jwforres @spadgett should be ready for review. Since the PageObjects are OOP, I updated to using es6 classes for everything (and let, const, fat arrows, etc.). Since its all in node no concerns about running with the "new shiny" JS stuff. 😄

Thoughts?

@benjaminapetersen
Copy link
Contributor Author

squashed & rebased.

@@ -89,7 +89,9 @@ exports.config = {
// run.
suites: {
// smoke: 'spec/smoketests/*.js',
rest_api: 'integration/rest_api/*.js' // This suite of tests should only require a running master api, it should not require a node
'add-to-project': 'integration/features/user_adds_template_to_project.spec.js',
'create-project': 'integration/features/user_creates_project.spec.js' // This suite of tests should only require a running master api, it should not require a node
Copy link
Member

Choose a reason for hiding this comment

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

these feel a bit specific for suite names to me, i feel like the split is typically going to be tests that require a working node and tests that don't. I can't remember the format for what a suite name can point to, is it only a single regexp string, or can it also be an array of strings?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it can be an array of strings. I think we can do both, but its really nice (and saves a lot of dev time) if you can pluck out one test flow at a time. I'm totally open to this growing/shrinking/shifting as we add tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, can use matchers + arrays or strings:

suites: {
  smoke: [
   'integration/features/userr_foo_bar.spec.js',
   'integration/features/user_baz.spec.js'
  ],
  'rest-api': [
    'integration/features/user/**/*.spec.js', // api specific match
  ],
  'one-off': '',
  'really-long-running-test': '' 
},

Copy link
Member

Choose a reason for hiding this comment

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

ok its fine for now but if we start getting a bunch of these we may want to organize them


}
visit() {
logger.log('visiting url:',this.getUrl());
Copy link
Member

Choose a reason for hiding this comment

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

nit but can you add a space after the comma before this.getUrl

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do.

clickAddToProject() {
let button = element(by.cssContainingText('.project-action-btn', 'Add to project'));
h.waitForElem(button);
// browser.wait(protractor.ExpectedConditions.elementToBeClickable(button), 2000);
Copy link
Member

Choose a reason for hiding this comment

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

reason this is there and commented out?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll eliminate

return element(by.cssContainingText('a', 'Storage')).click();
},
clickMonitoring: () => {
return element(by.cssContainingText('a', 'MOnitoring')).click();
Copy link
Member

Choose a reason for hiding this comment

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

typo on Monitoring, should be lower case 'o'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thx

clickOtherResources: () => {
return clickNestedMenuItem(by.cssContainingText('.dropdown-toggle', 'Resources'), by.cssContainingText('a', 'Other Resources'));
},
// rest
Copy link
Member

Choose a reason for hiding this comment

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

i wasnt really sure what this comment meant initially, could probably be more clear

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll update to // the rest of the top lvl menu items. It may have been good to just nest these for clarity, ie leftNav.apps.clickDeployment(), leftNav.builds.clickPipelines(). I'm open.

- `return new SomeOtherPage(this.project)` when a function will cause a navigation action
- Include a `browser.sleep(500)` before the return if the new page may take some time to load
- Encapsulate actions on the page as much as possible so test cases read simply
- Use `helpser/matchers.js` to encapsulate repetitious `expect()` logic
Copy link
Member

Choose a reason for hiding this comment

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

typo, helpers

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thx


exports.take = function(savePath) {
return browser.takeScreenshot().then(function(png) {
var stream = fs.createWriteStream(savePath || '/tmp/origin-web-console-e2e-screenshot.png');
Copy link
Member

Choose a reason for hiding this comment

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

we should avoid taking screenshots during tests except when failures occur, can you add a comment in here that says as much. Screenshots are taken automatically today when protractor sees a failure so you need a good reason to explicitly take another screenshot. Obviously also fine to use this for debugging purposes while writing tests as long as the calls are taken out before its merged

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add a // for debugging only comment


// Use logger functions in place of console to ensure that log messages are wrapped
// in a promise & resolved asyncronously.
// NOTE: even if you don't use .then(), the promise queue is still synchromized in protractor.
Copy link
Member

Choose a reason for hiding this comment

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

typo, synchronized

Copy link
Contributor Author

Choose a reason for hiding this comment

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

got it

// - the browse catalog flow can be run w/o needing this, however
exports.addNamespaceToCreateWhitelist = function(namespace) {
return browser.executeScript(function() {
window.CREATE_FROM_URL_WHITELIST.push(namespace);
Copy link
Member

Choose a reason for hiding this comment

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

isnt this window.OPENSHIFT_CONSTANTS.CREATE_FROM_URL_WHITELIST

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, definitely.

// waitForElement(element(by.css('.foo'))); // success
// waitForElement(element.all(by.css('.foos'))); // fail, incorrect element.all
var waitForElem = function(elem, timeout) {
return browser.wait(EC.presenceOf(elem), timeout || 5000, 'Element not found');
Copy link
Member

Choose a reason for hiding this comment

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

if this fails, what kind of error will you see, are you going to see the selector information somewhere to help track what failed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call! I'll update to 'Element not found: ' + elem.locator().toString(). That will print out something like this:

  - Error: Element not found: by.cssContainingText("a", "STUFF AND THINGS")

…t tests

- Use es6 classes to add PageObjects to organize tests
- Add h.waitForElem() and adjust h.waitForPresence() to allow for a callback fn
- Add helpers/scroll
- Add helpers/strings
- Add README for pageobjects
- Add base 'Page' PageObject for other pages to extend
- Add createFromTemplate PageObject
- Add deployments PageObject
- Update helpers/matchers, set hLevel in expectHeading(), add expectPartialHeading
- Add browser.sleep() to deleteAllProjects to ensure each project is removed
- Move all e2e test flows under test/integration/features/ and name them based on user actions
- Add xit to jshintrc
- Cleanup user_adds_template_to_project.spec
- Add numerous other helpers and page objects
@openshift-bot
Copy link

Evaluated for origin web console test up to 0c4ca98

@openshift-bot
Copy link

Origin Web Console Test Results: SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pull_requests_origin_web_console/987/) (Base Commit: e202d9f)

@jwforres
Copy link
Member

[merge]

@benjaminapetersen
Copy link
Contributor Author

WOOHOO! my life now has meaning. 😃

@openshift-bot
Copy link

openshift-bot commented Jan 25, 2017

Origin Web Console Merge Results: SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pull_requests_origin_web_console/987/) (Base Commit: e202d9f)

@openshift-bot
Copy link

Evaluated for origin web console merge up to 0c4ca98

@openshift-bot openshift-bot merged commit a2e9c6e into openshift:master Jan 25, 2017
@benjaminapetersen benjaminapetersen deleted the run-on-e2e-setup branch January 25, 2017 22:20
@jhadvig
Copy link
Member

jhadvig commented Jan 31, 2017

Looks great @benjaminapetersen
👍

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.

Update web console tests to use page objects and improve markup by adding hooks for QE
5 participants