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

[TS-SELENIUM] Cover the "Use Java IDE features and the inner loop" step from "Happy path" scenario #13615

Merged
merged 19 commits into from
Jul 5, 2019

Conversation

Ohrimenko1988
Copy link
Contributor

@Ohrimenko1988 Ohrimenko1988 commented Jun 21, 2019

What does this PR do?

Cover the "Use Java IDE features and the inner loop" step from "Happy path" scenario

Screenshot from 2019-07-01 15-21-16

What issues does this PR fix or reference?

Issue: #13519

Release Notes

Docs PR

@Ohrimenko1988 Ohrimenko1988 added status/code-review This issue has a pull request posted for it and is awaiting code review completion by the community. kind/task Internal things, technical debt, and to-do tasks to be performed. team/che-qe labels Jun 21, 2019
@Ohrimenko1988
Copy link
Contributor Author

ci-build

@Ohrimenko1988
Copy link
Contributor Author

ci-build

… path' scenario

Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
@Ohrimenko1988
Copy link
Contributor Author

ci-build

Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
@l0rd l0rd mentioned this pull request Jul 3, 2019
85 tasks
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
async closeTab(tabTitle: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) {
const tabCloseButtonLocator: By = By.xpath(`//div[text()='${tabTitle}']/parent::li//div[contains(@class, 'p-TabBar-tabCloseIcon')]`);
const tabCloseButtonLocator: By = this.getTabCloseIconLocator(tabTitle);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the logic moved to method which is used only once?

}, timeout);
}

public async waitHighlightedSuggestion(editorTabTitle: string,
Copy link
Contributor

Choose a reason for hiding this comment

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

This method is not used.

e2e/pageobjects/ide/Editor.ts Show resolved Hide resolved
@@ -140,9 +187,13 @@ export class Editor {
timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT,
polling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING) {

await this.ide.closeAllNotifications();
Copy link
Contributor

Choose a reason for hiding this comment

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

I would move this.ide.closeAllNotifications() to the method selectTab and use this method here instead of current code. So from currently:

await this.ide.closeAllNotifications();
await this.clickOnTab(editorTabTitle);
await this.waitTabFocused(editorTabTitle);

I would call await this.selectTab(editorTabTitle);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The selectTab method has been already implemented. Just put it here.

}

if (err instanceof error.WebDriverError) {
await this.driverHelper.wait(polling);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why should we wait when WebDriverError occures?

export class Terminal {
constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper) { }

async waitTab(tabTitle: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Some of the methods like waitTab or closeTab seems to be same as those in Editor. Maybe it would be better to move them to some another class to avoid code duplication.

Copy link
Contributor

Choose a reason for hiding this comment

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

Editor & terminal are two different "parts of IDE"... they may have these methods in common, but I don't think it would be wise to abstract this (make a common ancestor for these components), as these components are not very related....

});
});

suite.skip('Language server validation', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the skip there intended?

await driverHelper.wait(5000);

// await topMenu.selectOption('Terminal', 'Run Task...');
// await quickOpenContainer.clickOnContainerItem('che: run');
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are those comments left there?

const attempts: number = TestConstants.TS_SELENIUM_DEFAULT_ATTEMPTS;
const polling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING;

for (let i = 0; i < attempts; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please elaborate why await with timeout is not suitable and you have to use for loop here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because timeout assumes that wait will be failed when the timeout end. And try may be not repeated if waiting for the element is too long. In this case, I want to ensure that try will be repeated.

Copy link
Contributor

Choose a reason for hiding this comment

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

So the possible solution could be reduce the timeout for waiting for the elements inside the "waiting function" and increasing timeout for the wait, right? But yeah... As you mentioned under my comment here.. this could be done in other PR.

Copy link
Contributor

@rhopp rhopp left a comment

Choose a reason for hiding this comment

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

I've put some comments in here... Most of them are not blocking the PR, but I would like to have some reaction to them before merging the PR.

Also what I'm missing, when reading and - more importantly - running the tests is proper logging. I would like to know what is happening... The most obvious examples are the waiting functions... I would like to see in the logs what is currently being executed, how long are we waiting for different stuff etc. But this is something we would need to tackle in some other PR.

@@ -112,3 +112,6 @@ data:
JAEGER_SAMPLER_PARAM: "1"
JAEGER_REPORTER_MAX_QUEUE_SIZE: "10000"
CHE_METRICS_ENABLED: {{ .Values.global.metricsEnabled | quote }}
CHE_WORKSPACE_JAVA__OPTIONS: "-Xmx2000m"
CHE_WORKSPACE_MAVEN__OPTIONS: "-Xmx20000m"
CHE_INFRA_KUBERNETES_WORKSPACE__START__TIMEOUT__MIN: "15"
Copy link
Contributor

Choose a reason for hiding this comment

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

Have we tried that without this timeout? (Just with default one?) I would prefer having default one and put this extended one just in case the default proves to be not sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without this variables each launch fails with "heap space" error

Copy link
Contributor

Choose a reason for hiding this comment

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

I may have not been completely clear here... I was talking just about CHE_INFRA_KUBERNETES_WORKSPACE__START__TIMEOUT__MIN. This one is not affecting command execution (build inside workspace) in any way.


// take browser console logs and write to file
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice addition. +1 ;-)

const configurationItemLocator: By = By.xpath(`//select[@class='debug-configuration']//option[text()=\'${itemText}\']`);

// for ensure that drop-down list has been opened
await this.driverHelper.wait(2000);
Copy link
Contributor

Choose a reason for hiding this comment

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

Couldn't these "hard" waits be replaced by some conditional waits? I think having "dumb" sleeps in test code is not a good practice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case, it is not critical because it works properly even without this sleep. This is my overcautiousness for CI launches

Copy link
Contributor

Choose a reason for hiding this comment

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

I strongly believe any sleep like this should be either removed, or replaced by conditional wait.
These three waits in this method are perfect example of this... You even put into comments what should we wait for.
This is the strongest reason preventing me from giving approval to this PR. If it works OK without them (even on CI), we could remove them. If it doesn't work right without them, we should replace them with conditional waits.

return true;
} catch (err) {
const isTimeoutError: boolean = err instanceof error.TimeoutError;
if (!isTimeoutError) {
Copy link
Contributor

Choose a reason for hiding this comment

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

hehe.. just a nitpicking:

                if (! (err instanceof error.TimeoutError)) {
                    throw err;
                }

This version seems readable enough for me without needing to create new variable. This is not blocking this PR at all, I'm just expressing my opinion ;-)

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 used this variable for debug logging, logging was removed and the variable is left

return;
} catch (err) {
// ignore errors and wait
await this.driverHelper.wait(polling);
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we know what errors can happen here? Shouldn't we at least log them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case, we should ignore all exceptions

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah.. I can see that from the code, that we are ignoring all the errors... My question was "Why?"
I'm not saying this "silent swallowing" of errors have to be wrong. But usually it's a bad code smell and I think it needs more explanation.
Furthermore, there is quite a big chunk (relatively) of code in the try-catch block and I can imagine quite lot of possible errors could happen - having them logged could help in possible debugging.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because actions inside of try/catch are very sensitive, and I tried to process most common exceptions and final was that the "WebDriverError" may be happening and it does not make sense to process other exceptions because it is a parent of all driver exceptions. Anyway, if something went wrong exception with full explanation will be thrown in the further code.

await editor.waitTabFocused(javaFileName);
await editor.selectTab(javaFileName);

await ide.checkLsInitializationStart('Starting Java Language Server');
await ide.waitStatusBarTextAbcence('Starting Java Language Server', 360000);
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this is out of scope of this PR, but for some next time - Abcence doesn't exist - it's Absence

e2e/tests/e2e_happy_path/HappyPath.spec.ts Show resolved Hide resolved
}
}

// async function runTask(taskTitle: string, terminalTabTitle: string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Some forgotten commented code. Pls remove this from the final PR.

const attempts: number = TestConstants.TS_SELENIUM_DEFAULT_ATTEMPTS;
const polling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING;

for (let i = 0; i < attempts; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I know you have already lot of methods like this, where you implemented your polling mechanism, but is this really needed? Couldn't we just use https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/lib/webdriver.js#L448? (and as allways... I'm pretty new to this tech, so there may be reasons for this to be not possible... If yes, please tell me the reasons - I want to get wiser :-D)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similar question and ansver #13615 (comment) .
And a small addition. Most of the mentioned methods really maybe reworked by waits. I have already done something similar https://github.com/eclipse/che/blob/master/e2e/utils/DriverHelper.ts#L150-L170
It may be done in the separate PR

e2e/pageobjects/ide/Terminal.ts Show resolved Hide resolved
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Copy link
Contributor

@rhopp rhopp left a comment

Choose a reason for hiding this comment

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

I've put some more comments here... I'm also requesting changes, as I feel strongly about "hard sleeps" ( like driverHelper.wait(2000)).

components:
- type: kubernetes
alias: petclinic-web
reference: https://gist.githubusercontent.com/l0rd/52eda1d1b57878a218cba17c0c93a477/raw/9a2d56e777d22e77c25c45744caea35bb62d8688/petclinic.yaml
Copy link
Contributor

Choose a reason for hiding this comment

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

It's possible to have URL to raw file also by branch.. that may be better solution to this.

https://raw.githubusercontent.com/eclipse/che/master/e2e/files/happy-path/containers-happy-path.yaml

Copy link
Contributor

Choose a reason for hiding this comment

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

Thinking about it more... I guess this will come in followup PR, right? (To not break current state)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already changed locally to the branch URL

e2e/pageobjects/ide/Editor.ts Show resolved Hide resolved
return;
} catch (err) {
// ignore errors and wait
await this.driverHelper.wait(polling);
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah.. I can see that from the code, that we are ignoring all the errors... My question was "Why?"
I'm not saying this "silent swallowing" of errors have to be wrong. But usually it's a bad code smell and I think it needs more explanation.
Furthermore, there is quite a big chunk (relatively) of code in the try-catch block and I can imagine quite lot of possible errors could happen - having them logged could help in possible debugging.

elementStyleValue = elementStyleValue.replace('position: absolute; top: ', '');
elementStyleValue = elementStyleValue.replace('px; width: 100%; height: 19px;', '');

const lineYCoordinate: number = Number.parseInt(elementStyleValue, 10);
Copy link
Contributor

Choose a reason for hiding this comment

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

@Ohrimenko1988 Would be nice to respond even if you don't agree with the proposed solution ;-) I, for example, think the regexp solution is a bit more robust... If you think it's not needed to have more robust solution, please respond ;-)

await this.driverHelper.getDriver().wait(async () => {
const elementText: string = await this.driverHelper.waitAndGetText(statusBarLocator, timeout);
// for ensuring that check is not invoked in the gap of status displaying
for (let i: number = 0; i < 3; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@Ohrimenko1988 Explanation? I genuinely don't understand this piece of code and it doesn't make much sense to me...

@@ -112,3 +112,6 @@ data:
JAEGER_SAMPLER_PARAM: "1"
JAEGER_REPORTER_MAX_QUEUE_SIZE: "10000"
CHE_METRICS_ENABLED: {{ .Values.global.metricsEnabled | quote }}
CHE_WORKSPACE_JAVA__OPTIONS: "-Xmx2000m"
CHE_WORKSPACE_MAVEN__OPTIONS: "-Xmx20000m"
CHE_INFRA_KUBERNETES_WORKSPACE__START__TIMEOUT__MIN: "15"
Copy link
Contributor

Choose a reason for hiding this comment

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

I may have not been completely clear here... I was talking just about CHE_INFRA_KUBERNETES_WORKSPACE__START__TIMEOUT__MIN. This one is not affecting command execution (build inside workspace) in any way.

const configurationItemLocator: By = By.xpath(`//select[@class='debug-configuration']//option[text()=\'${itemText}\']`);

// for ensure that drop-down list has been opened
await this.driverHelper.wait(2000);
Copy link
Contributor

Choose a reason for hiding this comment

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

I strongly believe any sleep like this should be either removed, or replaced by conditional wait.
These three waits in this method are perfect example of this... You even put into comments what should we wait for.
This is the strongest reason preventing me from giving approval to this PR. If it works OK without them (even on CI), we could remove them. If it doesn't work right without them, we should replace them with conditional waits.

export class Terminal {
constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper) { }

async waitTab(tabTitle: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Editor & terminal are two different "parts of IDE"... they may have these methods in common, but I don't think it would be wise to abstract this (make a common ancestor for these components), as these components are not very related....

e2e/pageobjects/ide/Terminal.ts Show resolved Hide resolved
await this.selectTerminalTab(tabTitle, timeout);

// for ensure that element is interactable
await this.driverHelper.wait(3000);
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this is still something I strongly feel about... Until I'm proven we really need this and it couldn't be done any other way I would request this to change.

Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Signed-off-by: Ihor Okhrimenko <iokhrime@redhat.com>
Copy link
Contributor

@rhopp rhopp left a comment

Choose a reason for hiding this comment

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

There are still some questions/nitpicks unanswered, but all of them are of minor character.

@Ohrimenko1988 Ohrimenko1988 merged commit b045651 into master Jul 5, 2019
@Ohrimenko1988 Ohrimenko1988 deleted the selen-happy-4 branch July 5, 2019 08:35
@Ohrimenko1988 Ohrimenko1988 removed the status/code-review This issue has a pull request posted for it and is awaiting code review completion by the community. label Jul 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/task Internal things, technical debt, and to-do tasks to be performed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants