From: Pascal Mugnier Date: Thu, 29 Mar 2018 14:40:55 +0000 (+0200) Subject: SONAR-10039 Ease changing of password settings X-Git-Tag: 7.5~1409 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d11fb8ece1a77f028a7ba95004df46ce725a0c00;p=sonarqube.git SONAR-10039 Ease changing of password settings --- diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/settings/SettingsPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/settings/SettingsPage.java index bd03b0ea77d..9bb53d9b50f 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/settings/SettingsPage.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/settings/SettingsPage.java @@ -23,6 +23,9 @@ import com.codeborne.selenide.Condition; import com.codeborne.selenide.Selenide; import com.codeborne.selenide.SelenideElement; import org.openqa.selenium.By; +import org.openqa.selenium.Keys; + +import static com.codeborne.selenide.Selectors.byText; public class SettingsPage { @@ -55,6 +58,12 @@ public class SettingsPage { return this; } + public SettingsPage assertSettingValueIsNotedAsDefault(String settingKey) { + Selenide.$("[data-key=\"" + settingKey + "\"]").find(".spacer-top.note") + .shouldBe(Condition.text("(default)")); + return this; + } + public SettingsPage assertBooleanSettingValue(String settingKey, boolean value) { SelenideElement toggle = Selenide.$("button[name=\"settings[" + settingKey + "]\"]"); if (value) { @@ -65,6 +74,62 @@ public class SettingsPage { return this; } + public SettingsPage assertSettingValueCanBeSaved(String settingKey) { + Selenide.$("[data-key=\"" + settingKey + "\"]").find(byText("Save")) + .should(Condition.exist) + .shouldNotBe(Condition.attribute("disabled")); + return this; + } + + public SettingsPage assertSettingValueCannotBeSaved(String settingKey) { + Selenide.$("[data-key=\"" + settingKey + "\"]").find(byText("Save")) + .should(Condition.exist) + .shouldBe(Condition.attribute("disabled")); + return this; + } + + public SettingsPage assertSettingValueCanBeReset(String settingKey) { + Selenide.$("[data-key=\"" + settingKey + "\"]").find(byText("Reset")) + .should(Condition.exist); + return this; + } + + public SettingsPage assertSettingValueCanBeCanceled(String settingKey) { + Selenide.$("[data-key=\"" + settingKey + "\"]").find(byText("Cancel")) + .should(Condition.exist); + return this; + } + + public SettingsPage assertInputCount(String settingKey, int count) { + Selenide.$("[data-key=\"" + settingKey + "\"]").findAll("input").shouldHaveSize(count); + return this; + } + + public SettingsPage changeSettingValue(String settingKey, String value) { + Selenide.$("[data-key=\"" + settingKey + "\"]").find("input").val(value); + return this; + } + + public SettingsPage changeSettingValue(String settingKey, int index, String value) { + Selenide.$("[data-key=\"" + settingKey + "\"]").findAll("input").get(index).val(value); + return this; + } + + public SettingsPage clickOnCancel(String settingKey) { + Selenide.$("[data-key=\"" + settingKey + "\"]").find(byText("Cancel")).click(); + return this; + } + + public SettingsPage removeFirstValue(String settingKey) { + Selenide.$("[data-key=\"" + settingKey + "\"]").find(".button.js-remove-value.button-icon").click(); + return this; + } + + public SettingsPage sendDeleteKeyToSettingField(String settingKey) { + Selenide.$("[data-key=\"" + settingKey + "\"]").find("input").sendKeys(Keys.BACK_SPACE); + return this; + } + public SettingsPage setStringValue(String settingKey, String value) { SelenideElement setting = Selenide.$(".settings-definition[data-key=\"" + settingKey + "\"]"); setting.find("input").val(value); diff --git a/server/sonar-web/src/main/js/apps/settings/__tests__/DefinitionActions-test.tsx b/server/sonar-web/src/main/js/apps/settings/__tests__/DefinitionActions-test.tsx index d5ab2ec0ba7..f13959d9abf 100644 --- a/server/sonar-web/src/main/js/apps/settings/__tests__/DefinitionActions-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/__tests__/DefinitionActions-test.tsx @@ -69,16 +69,12 @@ it('displays reset button when empty and not default', () => { expect(wrapper).toMatchSnapshot(); }); -it('displays default value label when current value differs', () => { - const wrapper = shallowRender('foobar', true, false); - expect(wrapper).toMatchSnapshot(); -}); - function shallowRender(changedValue: string, hasError: boolean, isDefault: boolean) { return shallow( {}} onReset={() => {}} diff --git a/server/sonar-web/src/main/js/apps/settings/__tests__/__snapshots__/DefinitionActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/__tests__/__snapshots__/DefinitionActions-test.tsx.snap index 12095239f94..1a8c1227c20 100644 --- a/server/sonar-web/src/main/js/apps/settings/__tests__/__snapshots__/DefinitionActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/__tests__/__snapshots__/DefinitionActions-test.tsx.snap @@ -2,16 +2,6 @@ exports[`disables save button on error 1`] = ` -
- settings._default -
@@ -34,16 +24,6 @@ exports[`disables save button on error 1`] = ` exports[`displays cancel button when value changed and has error 1`] = ` -
- settings._default -
@@ -66,16 +46,6 @@ exports[`displays cancel button when value changed and has error 1`] = ` exports[`displays cancel button when value changed and no error 1`] = ` -
- settings._default -
@@ -112,39 +82,10 @@ exports[`displays default message when value is default 1`] = ` className="settings-definition-changes nowrap" > - -
-
-`; - -exports[`displays default value label when current value differs 1`] = ` - -
- - - - @@ -194,16 +122,6 @@ exports[`displays reset button when empty and not default 1`] = ` exports[`displays save button when it can be saved 1`] = ` -
- settings._default -
diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js index cde7870ee16..b2ed1582ada 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js @@ -29,8 +29,15 @@ export default class InputForPassword extends React.PureComponent { changing: false }; + componentWillReceiveProps(nextProps /*: Props*/) { + if (!nextProps.hasValueChanged && this.props.hasValueChanged) { + this.setState({ changing: false, value: '' }); + } + } + handleInputChange(e) { - this.setState({ value: e.target.value }); + this.props.onChange(e.target.value); + this.setState({ changing: true, value: e.target.value }); } handleChangeClick(e) { @@ -39,57 +46,34 @@ export default class InputForPassword extends React.PureComponent { this.setState({ changing: true }); } - handleCancelChangeClick(e) { - e.preventDefault(); - e.target.blur(); - this.setState({ changing: false, value: '' }); - } - - handleFormSubmit(e) { - e.preventDefault(); - this.props.onChange(this.state.value); - this.setState({ changing: false, value: '' }); - } - renderInput() { return ( -
-
this.handleFormSubmit(e)}> - - this.handleInputChange(e)} - /> - - - - this.handleCancelChangeClick(e)}> - {translate('cancel')} - -
-
+
+ + this.handleInputChange(e)} + type="password" + value={this.state.value} + /> +
); } render() { - if (this.state.changing) { + const hasValue = !!this.props.value; + + if (this.state.changing || !hasValue) { return this.renderInput(); } - const hasValue = !!this.props.value; - return (
- {hasValue && } - - + +
); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js index 0506676493b..b30d3db8cc9 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForPassword-test.js @@ -43,21 +43,6 @@ it('should open form', () => { expect(input.find('form').length).toBe(1); }); -it('should close form', () => { - const onChange = jest.fn(); - const input = shallow( - - ); - const button = input.find('button'); - expect(button.length).toBe(1); - - click(button); - expect(input.find('form').length).toBe(1); - - click(input.find('form').find('a')); - expect(input.find('form').length).toBe(0); -}); - it('should set value', () => { const onChange = jest.fn(() => Promise.resolve()); const input = shallow( diff --git a/tests/build.gradle b/tests/build.gradle index ae2b73cf2cc..666130b7e6f 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -102,6 +102,7 @@ task integrationTest(type: Test) { includeTestsMatching 'org.sonarqube.tests.authorization.*Suite' includeTestsMatching 'org.sonarqube.tests.measure.*Suite' includeTestsMatching 'org.sonarqube.tests.qualityGate.*Suite' + includeTestsMatching 'org.sonarqube.tests.settings.*Suite' includeTestsMatching 'org.sonarqube.tests.source.*Suite' break case 'Category2': diff --git a/tests/src/test/java/org/sonarqube/tests/settings/SettingsSuite.java b/tests/src/test/java/org/sonarqube/tests/settings/SettingsSuite.java new file mode 100644 index 00000000000..0b73df2a436 --- /dev/null +++ b/tests/src/test/java/org/sonarqube/tests/settings/SettingsSuite.java @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarqube.tests.settings; + +import com.sonar.orchestrator.Orchestrator; +import org.junit.ClassRule; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import util.ItUtils; + +import static util.ItUtils.xooPlugin; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + ValidatorsTest.class +}) +public class SettingsSuite { + + @ClassRule + public static final Orchestrator ORCHESTRATOR = ItUtils.newOrchestratorBuilder() + + .addPlugin(xooPlugin()) + + .build(); + +} diff --git a/tests/src/test/java/org/sonarqube/tests/settings/ValidatorsTest.java b/tests/src/test/java/org/sonarqube/tests/settings/ValidatorsTest.java new file mode 100644 index 00000000000..99bd3191ce9 --- /dev/null +++ b/tests/src/test/java/org/sonarqube/tests/settings/ValidatorsTest.java @@ -0,0 +1,128 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarqube.tests.settings; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.build.SonarScanner; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.sonarqube.qa.util.Tester; +import org.sonarqube.qa.util.pageobjects.settings.SettingsPage; +import org.sonarqube.tests.component.ComponentSuite; +import org.sonarqube.ws.Users; + +import static util.ItUtils.projectDir; + +public class ValidatorsTest { + + private Users.CreateWsResponse.User adminUser; + + @ClassRule + public static Orchestrator orchestrator = ComponentSuite.ORCHESTRATOR; + + @Rule + public Tester tester = new Tester(orchestrator).disableOrganizations(); + + @Before + public void initUsers() { + adminUser = tester.users().generateAdministrator(u -> u.setLogin("admin-user").setPassword("admin-user")); + tester.users().generate(u -> u.setLogin("random-user").setPassword("random-user")); + } + + @Test + public void main_settings_simple_value() { + SettingsPage page = tester.openBrowser().logIn().submitCredentials(adminUser.getLogin()) + .openSettings(null); + tester.wsClient().users().skipOnboardingTutorial(); + + String elementSelector = "sonar.dbcleaner.hoursBeforeKeepingOnlyOneSnapshotByDay"; + page.assertSettingValueIsNotedAsDefault(elementSelector) + .changeSettingValue(elementSelector, "1") + .assertSettingValueCanBeSaved(elementSelector) + .assertSettingValueCanBeCanceled(elementSelector); + + page.sendDeleteKeyToSettingField(elementSelector) + .assertSettingValueCannotBeSaved(elementSelector) + .assertSettingValueCanBeReset(elementSelector) + .assertSettingValueCanBeCanceled(elementSelector); + } + + @Test + public void main_settings_complex_value() { + SettingsPage page = tester.openBrowser().logIn().submitCredentials(adminUser.getLogin()) + .openSettings(null); + tester.wsClient().users().skipOnboardingTutorial(); + + String elementSelector = "sonar.preview.excludePlugins"; + page.assertSettingValueIsNotedAsDefault(elementSelector) + .removeFirstValue(elementSelector) + .assertSettingValueCanBeSaved(elementSelector) + .assertSettingValueCanBeCanceled(elementSelector); + + page.clickOnCancel(elementSelector).assertSettingValueIsNotedAsDefault(elementSelector); + } + + @Test + public void project_settings_simple_value() { + analyzeSample(); + + SettingsPage page = tester.openBrowser().logIn().submitCredentials(adminUser.getLogin()) + .openSettings("sample"); + tester.wsClient().users().skipOnboardingTutorial(); + + String elementSelector = "sonar.dbcleaner.hoursBeforeKeepingOnlyOneSnapshotByDay"; + page.assertSettingValueIsNotedAsDefault(elementSelector) + .changeSettingValue(elementSelector, "1") + .assertSettingValueCanBeSaved(elementSelector) + .assertSettingValueCanBeCanceled(elementSelector); + + page.sendDeleteKeyToSettingField(elementSelector) + .assertSettingValueCannotBeSaved(elementSelector) + .assertSettingValueCanBeReset(elementSelector) + .assertSettingValueCanBeCanceled(elementSelector); + } + + @Test + public void project_settings_complex_value() { + analyzeSample(); + + SettingsPage page = tester.openBrowser().logIn().submitCredentials(adminUser.getLogin()) + .openSettings("sample"); + tester.wsClient().users().skipOnboardingTutorial(); + + String elementSelector = "sonar.issue.ignore.allfile"; + page.openCategory("Analysis Scope") + .changeSettingValue(elementSelector, "foo") + .changeSettingValue(elementSelector, 1, "bar") + .assertSettingValueCanBeSaved(elementSelector) + .assertSettingValueCanBeCanceled(elementSelector); + + page.clickOnCancel(elementSelector).assertInputCount(elementSelector, 1); + } + + private void analyzeSample() { + SonarScanner scan = SonarScanner.create(projectDir("shared/xoo-sample")) + .setProperty("sonar.cpd.exclusions", "**/*"); + orchestrator.executeBuild(scan); + } + +}