This repository was archived by the owner on Oct 3, 2019. It is now read-only.
-
Couldn't load subscription status.
- Fork 28
Create UI test to verify Developer perspective #206
Open
pmacik
wants to merge
26
commits into
redhat-developer:master
Choose a base branch
from
pmacik:odc-807_test-ui-devperspective
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
7486686
test-ui-devperspective: Create UI test to verify Developer perspective
pmacik 834f711
test-ui-devperspective: Add github.com/tebeka/selenium to Gopkg.*
pmacik da10548
test-ui-devperspective: Update Gopkg.* with latest github.com/tebeka/…
pmacik 2fc8f73
test-ui-devperspective: Update steps before test.
pmacik 97900c9
test-ui-devperspective: Fix Go lint issues.
pmacik c24debb
test-ui-devperspective: Install chromedriver and chomium-headless to …
pmacik f3fb51c
test-ui-devperspective: Set CHROMEDRIVER_BINARY to full path
pmacik 26a1349
test-ui-devperspective: Set CHROMEDRIVER_BINARY to full path as a def…
pmacik b44f04e
Merge branch 'master' into odc-807_test-ui-devperspective
pmacik 93385e5
Merge branch 'master' into odc-807_test-ui-devperspective
pmacik 6826e6d
Merge branch 'master' into odc-807_test-ui-devperspective
pmacik f37e6d3
Merge branch 'master' into odc-807_test-ui-devperspective
pmacik 6442a2e
test-ui-devperspective: Split test for admin and non-admin user.
pmacik 744fb05
test-ui-devperspective: Updated log outputs for the console app.
pmacik df4f73c
Merge branch 'master' into odc-807_test-ui-devperspective
pmacik 2a5b245
test-ui-devperspective: Organize code into packages
pmacik 1cb9cea
test-ui-devperspective: Exclude the UI test from unit tests.
pmacik 375eff9
test-ui-devperspective: Include the UI test in e2e tests.
pmacik 83a06e5
test-ui-devperspective: Implement re-tries for FindElementBy func and…
pmacik 13f734a
Merge branch 'master' into odc-807_test-ui-devperspective
pmacik 97f867a
test-ui-devperspective: Harden the condition for FindElementBy function.
pmacik 2be9b3e
test-ui-devperspective: Fail test if for element is not found in Find…
pmacik 65c0827
test-ui-devperspective: Make test more verbose on what is doing.
pmacik 5faff40
Merge branch 'master' into odc-807_test-ui-devperspective
pmacik 171fe19
test-ui-devperspective: Remove the test from e2e tests. Let's keep th…
pmacik cfc022f
Merge branch 'master' into odc-807_test-ui-devperspective
pmacik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package support | ||
|
|
||
| import ( | ||
| "os" | ||
| "testing" | ||
| ) | ||
|
|
||
| // Getenv returns a value of environment variable, if it exists. | ||
| // Returns the default value otherwise. | ||
| func Getenv(t *testing.T, key string, defaultValue string) string { | ||
| value, found := os.LookupEnv(key) | ||
| var retVal string | ||
| if found { | ||
| retVal = value | ||
| } else { | ||
| retVal = defaultValue | ||
| } | ||
| t.Logf("Using env variable: %s=%s", key, retVal) | ||
| return retVal | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| package devperspective | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "strconv" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/redhat-developer/devconsole-operator/test/support" | ||
| "github.com/redhat-developer/devconsole-operator/test/ui" | ||
| "github.com/stretchr/testify/require" | ||
|
|
||
| "github.com/tebeka/selenium" | ||
| ) | ||
|
|
||
| var tag string | ||
|
|
||
| func TestDevPerspective(t *testing.T) { | ||
| tag = support.Getenv(t, "TAG", fmt.Sprintf("%d", time.Now().Unix())) | ||
| userIsAdmin := support.Getenv(t, "USER_IS_ADMIN", "true") | ||
| chBin := support.Getenv(t, "CHROMEDRIVER_BINARY", "/usr/bin/chromedriver") | ||
| chPort, err := strconv.Atoi(support.Getenv(t, "CHROMEDRIVER_PORT", "9515")) | ||
| require.NoError(t, err, "Chromedriver port") | ||
|
|
||
| devconsoleUsername := support.Getenv(t, "DEVCONSOLE_USERNAME", "consoledeveloper") | ||
| devconsolePassword := support.Getenv(t, "DEVCONSOLE_PASSWORD", "developer") | ||
| openshiftConsoleURL := support.Getenv(t, "OS_CONSOLE_URL", "http://localhost") | ||
|
|
||
| wd, svc := ui.InitSelenium( | ||
| t, | ||
| chBin, | ||
| chPort, | ||
| ) | ||
|
|
||
| defer tearDown(t, wd, svc) | ||
|
|
||
| defaultWait := 10 * time.Second | ||
|
|
||
| t.Logf("Open URL: %s", openshiftConsoleURL) | ||
| err = wd.Get(openshiftConsoleURL) | ||
| require.NoErrorf(t, err, "Open URL: %s", openshiftConsoleURL) | ||
| consoleIsUp := false | ||
| for attempt := 0; attempt < 10; attempt++ { | ||
| err = wd.Refresh() | ||
| require.NoErrorf(t, err, "Refresh URL: %s", openshiftConsoleURL) | ||
| el, _ := wd.FindElement(selenium.ByXPATH, "//*[contains(text(),'Application is not available')]") | ||
| if el != nil { | ||
| t.Logf("Openshift Console is not available, try again after 2s.") | ||
| time.Sleep(2 * time.Second) | ||
| } else { | ||
| t.Logf("Openshift Console is up.") | ||
| consoleIsUp = true | ||
| break | ||
| } | ||
| } | ||
| if !consoleIsUp { | ||
| require.FailNow(t, "Openshift Console is not available.") | ||
| } | ||
|
|
||
| require.NoError(t, err, fmt.Sprintf("Open console starting URL: %s", openshiftConsoleURL)) | ||
| ui.WaitForURLToContain(t, wd, "oauth", defaultWait) | ||
|
|
||
| var elem selenium.WebElement | ||
|
|
||
| if userIsAdmin == "true" { | ||
| elem = ui.FindElementBy(t, wd, selenium.ByLinkText, "kube:admin") | ||
| } else { | ||
| elem = ui.FindElementBy(t, wd, selenium.ByLinkText, devconsoleUsername) | ||
| } | ||
|
|
||
| ui.WaitForElementToBeDisplayed(t, wd, elem, defaultWait) | ||
| ui.ClickToElement(t, elem) | ||
|
|
||
| elem = ui.FindElementBy(t, wd, selenium.ByID, "inputUsername") | ||
| ui.WaitForElementToBeDisplayed(t, wd, elem, defaultWait) | ||
| ui.SendKeysToElement(t, elem, devconsoleUsername) | ||
|
|
||
| elem = ui.FindElementBy(t, wd, selenium.ByID, "inputPassword") | ||
| ui.WaitForElementToBeDisplayed(t, wd, elem, defaultWait) | ||
| ui.SendKeysToElement(t, elem, devconsolePassword) | ||
|
|
||
| elem = ui.FindElementBy(t, wd, selenium.ByXPATH, "//*/button[contains(text(),'Log In')]") | ||
| ui.ClickToElement(t, elem) | ||
|
|
||
| elem = ui.FindElementBy(t, wd, selenium.ByID, "nav-toggle") | ||
| ui.WaitForElementToBeDisplayed(t, wd, elem, defaultWait) | ||
| ui.ClickToElement(t, elem) | ||
|
|
||
| elem = ui.FindElementBy(t, wd, selenium.ByXPATH, "//*/a[@target][contains(text(),'Developer')]") | ||
| ui.WaitForElementToBeDisplayed(t, wd, elem, defaultWait) | ||
| } | ||
|
|
||
| func tearDown(t *testing.T, wd selenium.WebDriver, svc *selenium.Service) { | ||
| err := wd.Quit() | ||
| if err != nil { | ||
| t.Log(err) | ||
| } | ||
| err = svc.Stop() | ||
| if err != nil { | ||
| t.Log(err) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| package ui | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "fmt" | ||
| "image" | ||
| "image/png" | ||
| "os" | ||
| "path" | ||
| "strings" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/stretchr/testify/require" | ||
| "github.com/tebeka/selenium" | ||
| ) | ||
|
|
||
| //FindElementBy look for a web element by a given selector and returs it back when found. | ||
| func FindElementBy(t *testing.T, wd selenium.WebDriver, by string, selector string) selenium.WebElement { | ||
| t.Logf("Find element by %s=%s", by, selector) | ||
| maxAttempts := 10 | ||
| attemptInterval := 100 * time.Millisecond | ||
|
|
||
| // To avoid a problem where the element is yet not present | ||
| counter := 0 | ||
| for { | ||
| elems, err := wd.FindElements(by, selector) | ||
| if err != nil || len(elems) == 0 { | ||
| if counter <= maxAttempts { | ||
| t.Logf("Element for %s=%s not found, trying again...", by, selector) | ||
| time.Sleep(attemptInterval) | ||
| counter++ | ||
| } else { | ||
| require.NoError(t, fmt.Errorf("element for %s=%s not found", by, selector)) | ||
| } | ||
| } else { | ||
| return elems[0] | ||
| } | ||
| } | ||
| } | ||
|
|
||
| //WaitForElementToBeDisplayed for a given web element to be displayed/visible for a given time duration. | ||
| func WaitForElementToBeDisplayed(t *testing.T, wd selenium.WebDriver, element selenium.WebElement, duration time.Duration) { | ||
| t.Logf("Wait for element to be displayed") | ||
| err := wd.WaitWithTimeout(func(wd selenium.WebDriver) (bool, error) { | ||
| return element.IsDisplayed() | ||
| }, duration) | ||
| require.NoError(t, err, "Wait for element to be displayed") | ||
| } | ||
|
|
||
| //WaitForURLToContain waits for a current URL to contain the given text. It waits for a given time duration. | ||
| func WaitForURLToContain(t *testing.T, wd selenium.WebDriver, text string, duration time.Duration) { | ||
| t.Logf("Wait for URL to contain test '%s'...", text) | ||
| counter := 1 | ||
| err := wd.WaitWithTimeout(func(wd selenium.WebDriver) (bool, error) { | ||
| currentURL, err2 := wd.CurrentURL() | ||
| counter++ | ||
| return strings.Contains(currentURL, text), err2 | ||
| }, duration) | ||
| currentURL, err2 := wd.CurrentURL() | ||
| require.NoError(t, err2, fmt.Sprintf("Get current URL")) | ||
| require.NoError(t, err, fmt.Sprintf("Wait for URL to contain '%s'. The current URL is '%s'.", text, currentURL)) | ||
| } | ||
|
|
||
| //SendKeysToElement sends keys to a given web element | ||
| func SendKeysToElement(t *testing.T, element selenium.WebElement, keys string) { | ||
| t.Log("Send keys to element") | ||
| err := element.SendKeys(keys) | ||
| require.NoError(t, err, "Send keys to element") | ||
| } | ||
|
|
||
| //ClickToElement performs a click on a given web element. | ||
| func ClickToElement(t *testing.T, element selenium.WebElement) { | ||
| t.Log("Click to element") | ||
| err := element.Click() | ||
| require.NoError(t, err, "Click to element") | ||
| } | ||
|
|
||
| //InitSelenium creates and initializes a new ChromeDriver service. | ||
| func InitSelenium(t *testing.T, chromedriverPath string, chromedriverPort int) (selenium.WebDriver, *selenium.Service) { | ||
|
|
||
| service, err := selenium.NewChromeDriverService(chromedriverPath, chromedriverPort) | ||
| require.NoError(t, err) | ||
|
|
||
| chromeOptions := map[string]interface{}{ | ||
| "args": []string{ | ||
| "--verbose", | ||
| "--no-cache", | ||
| "--no-sandbox", | ||
| "--headless", | ||
| "--window-size=1920,1080", | ||
| "--window-position=0,0", | ||
| "--enable-features=NetworkService", // to ignore invalid HTTPS certificates | ||
| //"--whitelisted-ips=''", // to support running in a container | ||
| }, | ||
| } | ||
|
|
||
| caps := selenium.Capabilities{ | ||
| "browserName": "chrome", | ||
| "chromeOptions": chromeOptions, | ||
| } | ||
|
|
||
| wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", chromedriverPort)) | ||
| require.NoError(t, err) | ||
| return wd, service | ||
| } | ||
|
|
||
| //SaveScreenShotToPNG saves current screen to a given PNG file. | ||
| func SaveScreenShotToPNG(t *testing.T, wd selenium.WebDriver, filename string) { | ||
| t.Logf("Save screenshot to '%s'", filename) | ||
| err := os.MkdirAll(path.Dir(filename), 0775) | ||
| require.NoError(t, err, "Create screenshot directory") | ||
| // convert []byte to image for saving to file | ||
| imgByte, err := wd.Screenshot() | ||
| require.NoError(t, err, "Take screenshot") | ||
| img, _, _ := image.Decode(bytes.NewReader(imgByte)) | ||
|
|
||
| //save the imgByte to file | ||
| out, err := os.Create(filename) | ||
| defer close(t, out) | ||
|
|
||
| require.NoError(t, err, "Create a file for the screenshot") | ||
|
|
||
| err = png.Encode(out, img) | ||
| require.NoError(t, err, "Write screanshot to PNG file") | ||
| } | ||
|
|
||
| func close(t *testing.T, f *os.File) { | ||
| err := f.Close() | ||
| require.NoError(t, err, "Close output file") | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
One suggestion - I have noticed that there are intermittent timing issues related to the appearance of the UI elements in the Developer Perspective in the UI - I assume that this is due to the DOM being updated.
It would be a good idea for the test to be robust enough to avoid 'element not fund' errors. I'd suggest making this change:
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.
@ldimaggi Thanks for that idea. I implemented it in 83a06e5