-
Notifications
You must be signed in to change notification settings - Fork 232
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
Run on e2e setup, test add to project flow, prepwork for Run On OpenShift tests #1057
Conversation
104dd2e
to
24188ef
Compare
rebased |
beforeEach(function() { | ||
h.commonSetup(); | ||
h.login(); | ||
projectHelpers.deleteAllProjects(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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; | ||
}; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(); | ||
} | ||
}); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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; | ||
}, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree
There was a problem hiding this comment.
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); | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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..?
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the incorrect comment
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. |
fe3e19f
to
d024745
Compare
@spadgett second pass? other than the tabs comment, all should be fixed. |
var _ = require('lodash'); | ||
|
||
exports.startCase = function(str) { | ||
return str && _.startCase(str) || ''; |
There was a problem hiding this comment.
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 ''
)
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
d024745
to
bd9f469
Compare
[test] |
|
Interesting, gonna |
bd9f469
to
8b7c56c
Compare
@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. |
3a2dfd0
to
0130d75
Compare
Ok, passed once, removing an extra timeout & [TEST] one more time |
[TEST] |
[TEST] with a shorter sleep hack |
d73d32b
to
387b142
Compare
387b142
to
27fd707
Compare
[TEST] |
27fd707
to
05f1fe9
Compare
[TEST] |
[TEST] |
b7a3d0b
to
0fa463e
Compare
[TEST] |
0fa463e
to
063fb0d
Compare
063fb0d
to
4ead7da
Compare
squashed & rebased. |
4ead7da
to
552d4b5
Compare
@@ -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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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': ''
},
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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'
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo, helpers
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo, synchronized
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
552d4b5
to
0c4ca98
Compare
Evaluated for origin web console test up to 0c4ca98 |
Origin Web Console Test Results: SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pull_requests_origin_web_console/987/) (Base Commit: e202d9f) |
[merge] |
WOOHOO! my life now has meaning. 😃 |
Origin Web Console Merge Results: SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pull_requests_origin_web_console/987/) (Base Commit: e202d9f) |
Evaluated for origin web console merge up to 0c4ca98 |
Looks great @benjaminapetersen |
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./page-objects
directory contains file that understand how to interact with individual pages in the web console/helpers
dir contains some shared helpers/fixtures
dir contains support dataIf 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