From 4cdfb601921a808edd22f6e391ce4a72d8567140 Mon Sep 17 00:00:00 2001 From: Borychev Andrey Date: Sun, 16 Jun 2024 00:46:03 +0300 Subject: [PATCH 01/71] [#260] fix workspace email validation message interpolation with test --- .../io/hexlet/typoreporter/web/model/WorkspaceUserModel.java | 3 ++- .../java/io/hexlet/typoreporter/web/WorkspaceControllerIT.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java b/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java index c58c57f0..17d088a7 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java @@ -12,6 +12,7 @@ @NoArgsConstructor public class WorkspaceUserModel { - @Email(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", message = "The email \"{0}\" is not valid") + @Email(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", + message = "The email \"${validatedValue}\" is not valid") private String email; } diff --git a/src/test/java/io/hexlet/typoreporter/web/WorkspaceControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/WorkspaceControllerIT.java index d27d4211..7c90e348 100644 --- a/src/test/java/io/hexlet/typoreporter/web/WorkspaceControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/WorkspaceControllerIT.java @@ -403,7 +403,7 @@ void addUserNonValidEmailTest() throws Exception { .with(csrf())) .andReturn(); var body = response.getResponse().getContentAsString(); - assertThat(body).contains("The email %s is not valid", ACCOUNT_INCORRECT_EMAIL); + assertThat(body).contains(String.format("The email "%s" is not valid", ACCOUNT_INCORRECT_EMAIL)); } } From c20d43ea0f55b2f6203ce6f531746bd4686c7cb0 Mon Sep 17 00:00:00 2001 From: Borychev Andrey Date: Sun, 16 Jun 2024 21:19:52 +0300 Subject: [PATCH 02/71] [#260] fix email validation interpolation and tests --- .../service/dto/account/UpdateProfile.java | 3 +- .../web/model/SignupAccountModel.java | 3 +- .../typoreporter/web/AccountControllerIT.java | 34 ++++++++++++------- .../typoreporter/web/SignupControllerIT.java | 17 ++++++++++ 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java b/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java index 5fa41634..9f16a1fc 100644 --- a/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java +++ b/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java @@ -16,7 +16,8 @@ public class UpdateProfile { @AccountUsername private String username; - @Email(regexp = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", message = "The email \"{0}\" incorrect") + @Email(regexp = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", + message = "The email \"${validatedValue}\" is not valid") private String email; @NotBlank diff --git a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java index 16e5edee..b475a10b 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java @@ -26,7 +26,8 @@ public class SignupAccountModel { @AccountUsername private String username; - @Email(regexp = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", message = "The email \"{0}\" incorrect") + @Email(regexp = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", + message = "The email \"${validatedValue}\" is not valid") private String email; @AccountPassword diff --git a/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java index 2e1328df..e2773fd5 100644 --- a/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java @@ -20,8 +20,10 @@ import static io.hexlet.typoreporter.test.Constraints.POSTGRES_IMAGE; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc @@ -67,17 +69,21 @@ void updateAccountWithWrongEmailDomain() throws Exception { .isEqualTo(correctEmailDomain); String wrongEmailDomain = "test@test"; - mockMvc.perform(post("/account/update") - .param("username", userName) - .param("email", wrongEmailDomain) - .param("password", password) - .param("confirmPassword", password) - .param("firstName", userName) - .param("lastName", userName) - .with(csrf())); + + var response = mockMvc.perform(put("/account/update") + .param("username", userName) + .param("email", wrongEmailDomain) + .param("firstName", userName) + .param("lastName", userName) + .with(user(correctEmailDomain)) + .with(csrf())) + .andExpect(status().isOk()) + .andReturn(); + var body = response.getResponse().getContentAsString(); assertThat(accountRepository.findAccountByEmail(wrongEmailDomain)).isEmpty(); assertThat(accountRepository.findAccountByEmail(correctEmailDomain).orElseThrow().getEmail()) .isEqualTo(correctEmailDomain); + assertThat(body).contains(String.format("The email "%s" is not valid", wrongEmailDomain)); } @Test @@ -99,11 +105,13 @@ void updateAccountEmailUsingDifferentCase() throws Exception { assertThat(accountRepository.findAccountByEmail(emailLowerCase)).isNotEmpty(); mockMvc.perform(put("/account/update") - .param("firstName", username) - .param("lastName", username) - .param("username", username) - .param("email", emailUpperCase) - .with(csrf())); + .param("firstName", username) + .param("lastName", username) + .param("username", username) + .param("email", emailUpperCase) + .with(user(emailLowerCase)) + .with(csrf())) + .andExpect(status().isFound()); assertThat(accountRepository.findAccountByEmail(emailUpperCase)).isEmpty(); assertThat(accountRepository.findAccountByEmail(emailLowerCase)).isNotEmpty(); } diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index 81ce229d..d0b5ce1b 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -18,6 +18,7 @@ import org.testcontainers.containers.PostgreSQLContainer; import static com.github.database.rider.core.api.configuration.Orthography.LOWERCASE; +import static io.hexlet.typoreporter.test.factory.EntitiesFactory.ACCOUNT_INCORRECT_EMAIL; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.assertj.core.api.Assertions.assertThat; @@ -123,4 +124,20 @@ void createAccountWithWrongPassword() throws Exception { assertThat(accountRepository.findAccountByEmail(email)).isEmpty(); } + + @Test + void signupInAccountWithBadEmail() throws Exception { + model.setEmail(ACCOUNT_INCORRECT_EMAIL); + var response = mockMvc.perform(post("/signup") + .param("username", model.getUsername()) + .param("email", model.getEmail()) + .param("password", model.getPassword()) + .param("confirmPassword", model.getConfirmPassword()) + .param("firstName", model.getFirstName()) + .param("lastName", model.getLastName()) + .with(csrf())) + .andReturn(); + var body = response.getResponse().getContentAsString(); + assertThat(body).contains(String.format("The email "%s" is not valid", ACCOUNT_INCORRECT_EMAIL)); + } } From 9f25184cd226f74d2c26fa05623e30b7f5e9aa1d Mon Sep 17 00:00:00 2001 From: Borychev Andrey Date: Sun, 23 Jun 2024 22:23:01 +0300 Subject: [PATCH 03/71] [#260] unified error display format and add instancio, faker dependency for tests --- build.gradle.kts | 4 +- .../account/constraint/AccountUsername.java | 2 +- .../web/model/SignupAccountModel.java | 3 +- .../web/model/WorkspaceUserModel.java | 2 +- src/main/resources/messages_en.properties | 3 + src/main/resources/messages_ru.properties | 2 + .../resources/templates/account/signup.html | 12 +-- .../resources/templates/error-general.html | 2 +- src/main/resources/templates/login.html | 2 +- .../templates/widget/report-typo-error.html | 2 +- .../resources/templates/widget/typo-form.html | 2 +- .../templates/workspace/wks-settings.html | 2 +- .../templates/workspace/wks-users.html | 2 +- .../typoreporter/config/TestConfig.java | 18 ++++ .../test/factory/AccountModelGenerator.java | 46 ++++++++ .../typoreporter/utils/BundleSourceUtils.java | 75 +++++++++++++ .../hexlet/typoreporter/utils/ModelUtils.java | 32 ++++++ .../typoreporter/web/AccountControllerIT.java | 101 ++++++++++-------- 18 files changed, 249 insertions(+), 63 deletions(-) create mode 100644 src/test/java/io/hexlet/typoreporter/config/TestConfig.java create mode 100644 src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java create mode 100644 src/test/java/io/hexlet/typoreporter/utils/BundleSourceUtils.java create mode 100644 src/test/java/io/hexlet/typoreporter/utils/ModelUtils.java diff --git a/build.gradle.kts b/build.gradle.kts index 11b3e6ec..10ef00f0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,7 +48,9 @@ dependencies { implementation("org.mapstruct:mapstruct:1.5.3.Final") // Annotation processors annotationProcessor("org.mapstruct:mapstruct-processor:1.5.3.Final") - + //Generating models for tests + implementation("org.instancio:instancio-junit:3.6.0") + implementation("net.datafaker:datafaker:2.0.2") // Testing testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.security:spring-security-test") diff --git a/src/main/java/io/hexlet/typoreporter/domain/account/constraint/AccountUsername.java b/src/main/java/io/hexlet/typoreporter/domain/account/constraint/AccountUsername.java index 7520cca1..370d3d02 100644 --- a/src/main/java/io/hexlet/typoreporter/domain/account/constraint/AccountUsername.java +++ b/src/main/java/io/hexlet/typoreporter/domain/account/constraint/AccountUsername.java @@ -13,7 +13,7 @@ import java.lang.annotation.Target; @NotBlank -@Pattern(regexp = "^[-_A-Za-z0-9]*$") +@Pattern(regexp = "^[-_A-Za-z0-9]*$", message = "{validation.alert.wrong-username-pattern}") @Size(min = 2, max = 20) @Constraint(validatedBy = {}) @Target({ElementType.FIELD, ElementType.PARAMETER}) diff --git a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java index b475a10b..00d7a39e 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java @@ -27,7 +27,8 @@ public class SignupAccountModel { private String username; @Email(regexp = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", - message = "The email \"${validatedValue}\" is not valid") + //message = "The email \"${validatedValue}\" is not valid") + message = "{validation.alert.wrong-email}") private String email; @AccountPassword diff --git a/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java b/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java index 17d088a7..1b424db1 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java @@ -13,6 +13,6 @@ public class WorkspaceUserModel { @Email(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", - message = "The email \"${validatedValue}\" is not valid") + message = "{validation.alert.wrong-email}") private String email; } diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index 892b32df..eadf47ff 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -104,3 +104,6 @@ btn.delete-from-wks=Delete from workspace alert.password-wrong-format=Password must be between 8 and 20 characters \ and contain only latin letters, digits and symbols ~`!@#$%^&*()_-+={[}]|\:;"'<,>.?/ + +validation.alert.wrong-email=The email "${validatedValue}" is not valid +validation.alert.wrong-username-pattern=The pattern of username must be "{regexp}" diff --git a/src/main/resources/messages_ru.properties b/src/main/resources/messages_ru.properties index 65312c97..d36b20fe 100644 --- a/src/main/resources/messages_ru.properties +++ b/src/main/resources/messages_ru.properties @@ -102,3 +102,5 @@ text.wks-delete-confirm=Удалить пространство? alert.password-wrong-format=Пароль должен быть от 8 до 20 символов\ \ и содержать только буквы латинского алфавита,\ \ цифры и символы ~`!@#$%^&*()_-+={[}]|\:;"'<,>.?/ +validation.alert.wrong-email=Электронная почта "${validatedValue}" указана некорректно +validation.alert.wrong-username-pattern=Имя пользователя должен быть подходить под шаблон "{regexp}" diff --git a/src/main/resources/templates/account/signup.html b/src/main/resources/templates/account/signup.html index 600dfb40..328c8fd2 100644 --- a/src/main/resources/templates/account/signup.html +++ b/src/main/resources/templates/account/signup.html @@ -13,7 +13,7 @@ th:classappend="${!#fields.hasErrors('username') && formModified}? 'is-valid'" th:errorclass="is-invalid"> -
+

@@ -24,7 +24,7 @@ th:classappend="${!#fields.hasErrors('email') && formModified}? 'is-valid'" th:errorclass="is-invalid"> -
+

@@ -35,7 +35,7 @@ th:classappend="${!#fields.hasErrors('firstName') && formModified}? 'is-valid'" th:errorclass="is-invalid"> -
+

@@ -46,7 +46,7 @@ th:classappend="${!#fields.hasErrors('lastName') && formModified}? 'is-valid'" th:errorclass="is-invalid"> -
+

@@ -55,7 +55,7 @@ -
+

@@ -64,7 +64,7 @@ -
+

diff --git a/src/main/resources/templates/error-general.html b/src/main/resources/templates/error-general.html index 9fbfdc1c..e8b7e77e 100644 --- a/src/main/resources/templates/error-general.html +++ b/src/main/resources/templates/error-general.html @@ -2,7 +2,7 @@ -
+
diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 5905aa22..a81f88dc 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -6,7 +6,7 @@
- +
diff --git a/src/main/resources/templates/widget/report-typo-error.html b/src/main/resources/templates/widget/report-typo-error.html index 2d524213..b9aeb711 100755 --- a/src/main/resources/templates/widget/report-typo-error.html +++ b/src/main/resources/templates/widget/report-typo-error.html @@ -2,7 +2,7 @@ -
+
diff --git a/src/main/resources/templates/widget/typo-form.html b/src/main/resources/templates/widget/typo-form.html index a56b617f..02ea2f1b 100755 --- a/src/main/resources/templates/widget/typo-form.html +++ b/src/main/resources/templates/widget/typo-form.html @@ -17,7 +17,7 @@ type="text" > -
+

diff --git a/src/main/resources/templates/workspace/wks-settings.html b/src/main/resources/templates/workspace/wks-settings.html index 0881b0d1..1c4ac2e3 100644 --- a/src/main/resources/templates/workspace/wks-settings.html +++ b/src/main/resources/templates/workspace/wks-settings.html @@ -49,7 +49,7 @@
-
    +
diff --git a/src/main/resources/templates/workspace/wks-users.html b/src/main/resources/templates/workspace/wks-users.html index 271b1b8f..b7023c12 100644 --- a/src/main/resources/templates/workspace/wks-users.html +++ b/src/main/resources/templates/workspace/wks-users.html @@ -27,7 +27,7 @@ placeholder="Enter user email. For example: hexlet@gmail.com" th:field="*{email}" th:classappend="${!#fields.hasErrors('email') && formModified}? 'is-valid'" th:errorclass="is-invalid" required> -
+

diff --git a/src/test/java/io/hexlet/typoreporter/config/TestConfig.java b/src/test/java/io/hexlet/typoreporter/config/TestConfig.java new file mode 100644 index 00000000..c9485f64 --- /dev/null +++ b/src/test/java/io/hexlet/typoreporter/config/TestConfig.java @@ -0,0 +1,18 @@ +package io.hexlet.typoreporter.config; + +import net.datafaker.Faker; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; + +@Configuration +public class TestConfig { + @Bean + public Faker getFaker() { + return new Faker(); + } + @Bean + public ObjectMapper getObjectMapper() { + return new ObjectMapper(); + } +} diff --git a/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java b/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java new file mode 100644 index 00000000..43aa6942 --- /dev/null +++ b/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java @@ -0,0 +1,46 @@ +package io.hexlet.typoreporter.test.factory; + +import io.hexlet.typoreporter.web.model.SignupAccountModel; +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import net.datafaker.Faker; +import org.instancio.Instancio; +import org.instancio.Model; +import org.instancio.Select; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.Serializable; + +import static io.hexlet.typoreporter.test.factory.EntitiesFactory.ACCOUNT_INCORRECT_EMAIL; + +@Component +@Getter +public class AccountModelGenerator { + private Model correctAccountModel; + private Model incorrectAccountModel; + @Autowired + private Faker faker; + + @PostConstruct + public void init() { + final String password = faker.internet().password(8, 20); + final String incorrectPassword = faker.internet().password(1, 7); + correctAccountModel = Instancio.of(SignupAccountModel.class) + .supply(Select.field(SignupAccountModel::getUsername), () -> faker.name().firstName()) + .supply(Select.field(SignupAccountModel::getEmail), () -> faker.internet().emailAddress()) + .supply(Select.field(SignupAccountModel::getPassword), () -> password) + .supply(Select.field(SignupAccountModel::getConfirmPassword), () -> password) + .supply(Select.field(SignupAccountModel::getFirstName), () -> faker.name().firstName()) + .supply(Select.field(SignupAccountModel::getLastName), () -> faker.name().lastName()) + .toModel(); + incorrectAccountModel = Instancio.of(SignupAccountModel.class) + .supply(Select.field(SignupAccountModel::getUsername), () -> faker.name().firstName() + ".") + .supply(Select.field(SignupAccountModel::getEmail), () -> ACCOUNT_INCORRECT_EMAIL) + .supply(Select.field(SignupAccountModel::getPassword), () -> incorrectPassword) + .supply(Select.field(SignupAccountModel::getConfirmPassword), () -> incorrectPassword) + .supply(Select.field(SignupAccountModel::getFirstName), () -> "") + .supply(Select.field(SignupAccountModel::getLastName), () -> "") + .toModel(); + } +} diff --git a/src/test/java/io/hexlet/typoreporter/utils/BundleSourceUtils.java b/src/test/java/io/hexlet/typoreporter/utils/BundleSourceUtils.java new file mode 100644 index 00000000..bcbf0e50 --- /dev/null +++ b/src/test/java/io/hexlet/typoreporter/utils/BundleSourceUtils.java @@ -0,0 +1,75 @@ +package io.hexlet.typoreporter.utils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.stereotype.Component; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Component +public class BundleSourceUtils { + @Autowired + private MessageSource messageSource; + + /** + * @param key - Ключ из messages_LANG.properties, по которому необходимо получить значение + * Для RU необходимо использовать new Locale("ru") + * @param locale - Параметр, по которому определяется язык (локализация) + * @param isReplaceToQuot - Признак необходимости замены " на " + * @param args - Аргументы с типом String, которые необходимо подставить при интерполяции + * @return Строка из resource bundle изменениями + */ + public String getValueByKey(String key, Locale locale, boolean isReplaceToQuot, String[] args) { + String value = messageSource.getMessage(key, null, locale); + if (args != null) { + Pattern pattern = Pattern.compile("(\\$\\{(\\w+)\\})|(\\{(\\w+)\\})"); + Matcher matcher = pattern.matcher(value); + while (matcher.find()) { + value = value.replace(matcher.group(), "%s"); + } + value = isReplaceToQuot + ? value.replace("\"", """) + : value; + return String.format(value, args); + } + return isReplaceToQuot ? value.replace("\"", """) : value; + } + + /** + * @param key - Ключ из messages_LANG.properties, по которому необходимо получить значение + * @param isReplaceToQuot - Признак необходимости замены " на " + * @param args - Аргументы с типом String, которые необходимо подставить при интерполяции + * @return - Строка из resource bundle изменениями, по умолчанию на английском языке + */ + public String getValueByKey(String key, boolean isReplaceToQuot, String[] args) { + return getValueByKey(key, Locale.ENGLISH, isReplaceToQuot, args); + } + + /** + * @param key - Ключ из messages_LANGUAGE.properties, по которому необходимо получить значение. + * @param isReplaceToQuot - Признак необходимости замены " на " + * @return - Строка из resource bundle изменениями, по умолчанию на английском языке + */ + public String getValueByKey(String key, boolean isReplaceToQuot) { + return getValueByKey(key, isReplaceToQuot, null); + } + + /** + * @param key - Ключ из messages_LANG.properties, по которому необходимо получить значение + * @param args - Аргументы с типом String, которые необходимо подставить при интерполяции + * @return - Строка из resource bundle, по умолчанию на английском языке + */ + public String getValueByKey(String key, String[] args) { + return getValueByKey(key, false, args); + } + + /** + * @param key - Ключ из messages_LANG.properties, по которому необходимо получить значение + * @return - Строка из resource bundle, по умолчанию на английском языке + */ + public String getValueByKey(String key) { + return getValueByKey(key, null); + } +} diff --git a/src/test/java/io/hexlet/typoreporter/utils/ModelUtils.java b/src/test/java/io/hexlet/typoreporter/utils/ModelUtils.java new file mode 100644 index 00000000..98ee2042 --- /dev/null +++ b/src/test/java/io/hexlet/typoreporter/utils/ModelUtils.java @@ -0,0 +1,32 @@ +package io.hexlet.typoreporter.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import java.util.Map; +import java.util.Set; + +@Component +public class ModelUtils { + @Autowired + private ObjectMapper objectMapper; + + public MultiValueMap toFormParams(Object dto) throws Exception { + return toFormParams(dto, Set.of()); + } + + public MultiValueMap toFormParams(Object dto, Set excludeFields) throws Exception { + ObjectReader reader = objectMapper.readerFor(Map.class); + Map map = reader.readValue(objectMapper.writeValueAsString(dto)); + + MultiValueMap multiValueMap = new LinkedMultiValueMap<>(); + map.entrySet().stream() + .filter(e -> !excludeFields.contains(e.getKey())) + .forEach(e -> multiValueMap.add(e.getKey(), (e.getValue() == null ? "" : e.getValue()))); + return multiValueMap; + } +} diff --git a/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java index e2773fd5..2e91ee63 100644 --- a/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java @@ -4,10 +4,16 @@ import com.github.database.rider.spring.api.DBRider; import io.hexlet.typoreporter.repository.AccountRepository; import io.hexlet.typoreporter.test.DBUnitEnumPostgres; +import io.hexlet.typoreporter.test.factory.AccountModelGenerator; +import io.hexlet.typoreporter.utils.BundleSourceUtils; +import io.hexlet.typoreporter.utils.ModelUtils; +import io.hexlet.typoreporter.web.model.SignupAccountModel; +import org.instancio.Instancio; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.web.servlet.MockMvc; @@ -51,68 +57,69 @@ static void dataSourceProperties(DynamicPropertyRegistry registry) { @Autowired private AccountRepository accountRepository; + @Autowired + private AccountModelGenerator accountGenerator; + + @Autowired + private ModelUtils modelUtils; + @Autowired + private BundleSourceUtils bundleSourceUtils; + @Test void updateAccountWithWrongEmailDomain() throws Exception { - String userName = "testUser"; - String correctEmailDomain = "test@test.test"; - String password = "_Qwe1234"; + SignupAccountModel correctAccount = Instancio.of(accountGenerator.getCorrectAccountModel()).create(); + var correctFormParams = modelUtils.toFormParams(correctAccount); mockMvc.perform(post("/signup") - .param("username", userName) - .param("email", correctEmailDomain) - .param("password", password) - .param("confirmPassword", password) - .param("firstName", userName) - .param("lastName", userName) - .with(csrf())); - assertThat(accountRepository.findAccountByEmail(correctEmailDomain)).isNotEmpty(); - assertThat(accountRepository.findAccountByEmail(correctEmailDomain).orElseThrow().getEmail()) - .isEqualTo(correctEmailDomain); - - String wrongEmailDomain = "test@test"; + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .params(correctFormParams) + .with(csrf())) + .andExpect(status().isFound()); + assertThat(accountRepository.findAccountByEmail(correctAccount.getEmail())).isNotEmpty(); + assertThat(accountRepository.findAccountByEmail(correctAccount.getEmail()).orElseThrow().getEmail()) + .isEqualTo(correctAccount.getEmail()); + SignupAccountModel incorrectAccount = Instancio.of(accountGenerator.getIncorrectAccountModel()).create(); + var incorrectFormParams = modelUtils.toFormParams(incorrectAccount); var response = mockMvc.perform(put("/account/update") - .param("username", userName) - .param("email", wrongEmailDomain) - .param("firstName", userName) - .param("lastName", userName) - .with(user(correctEmailDomain)) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .params(incorrectFormParams) + .with(user(correctAccount.getEmail())) .with(csrf())) .andExpect(status().isOk()) .andReturn(); + var body = response.getResponse().getContentAsString(); - assertThat(accountRepository.findAccountByEmail(wrongEmailDomain)).isEmpty(); - assertThat(accountRepository.findAccountByEmail(correctEmailDomain).orElseThrow().getEmail()) - .isEqualTo(correctEmailDomain); - assertThat(body).contains(String.format("The email "%s" is not valid", wrongEmailDomain)); + assertThat(accountRepository.findAccountByEmail(incorrectAccount.getEmail())).isEmpty(); + assertThat(accountRepository.findAccountByEmail(correctAccount.getEmail()).orElseThrow().getEmail()) + .isEqualTo(correctAccount.getEmail()); + assertThat(body).contains(bundleSourceUtils.getValueByKey( + "validation.alert.wrong-email", true, new String[]{incorrectAccount.getEmail()})); + assertThat(body).contains(bundleSourceUtils.getValueByKey( + "validation.alert.wrong-username-pattern", true, new String[]{"^[-_A-Za-z0-9]*$"})); } @Test void updateAccountEmailUsingDifferentCase() throws Exception { - final String username = "testUser"; - final String emailUpperCase = "TEST@TEST.RU"; - final String emailMixedCase = "TEST@test.Ru"; - final String emailLowerCase = "test@test.ru"; - final String password = "_Qwe1234"; - + final SignupAccountModel account = Instancio.of(accountGenerator.getCorrectAccountModel()).create(); + account.setEmail(account.getEmail().toUpperCase()); + var accountFormParams = modelUtils.toFormParams(account); mockMvc.perform(post("/signup") - .param("username", username) - .param("email", emailMixedCase) - .param("password", password) - .param("confirmPassword", password) - .param("firstName", username) - .param("lastName", username) - .with(csrf())); - assertThat(accountRepository.findAccountByEmail(emailLowerCase)).isNotEmpty(); + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .params(accountFormParams) + .with(csrf())) + .andExpect(status().isFound()); + assertThat(accountRepository.findAccountByEmail(account.getEmail().toLowerCase())).isNotEmpty(); + final SignupAccountModel accountToUpdate = Instancio.of(accountGenerator.getCorrectAccountModel()).create(); + accountToUpdate.setEmail(account.getEmail().toLowerCase()); + accountFormParams = modelUtils.toFormParams(accountToUpdate); mockMvc.perform(put("/account/update") - .param("firstName", username) - .param("lastName", username) - .param("username", username) - .param("email", emailUpperCase) - .with(user(emailLowerCase)) - .with(csrf())) - .andExpect(status().isFound()); - assertThat(accountRepository.findAccountByEmail(emailUpperCase)).isEmpty(); - assertThat(accountRepository.findAccountByEmail(emailLowerCase)).isNotEmpty(); + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .params(accountFormParams) + .with(user(account.getEmail())) + .with(csrf())) + .andExpect(status().isOk()); + assertThat(accountRepository.findAccountByEmail(accountToUpdate.getEmail().toUpperCase())).isEmpty(); + assertThat(accountRepository.findAccountByEmail(accountToUpdate.getEmail())).isNotEmpty(); } } From bebe40a0affbfb8a23bf4d7f1c95aed6ea742dcc Mon Sep 17 00:00:00 2001 From: Borychev Andrey Date: Sun, 23 Jun 2024 22:28:51 +0300 Subject: [PATCH 04/71] [#260] fix linter and code clean up --- .../hexlet/typoreporter/test/factory/AccountModelGenerator.java | 2 -- src/test/java/io/hexlet/typoreporter/web/LoginIT.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java b/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java index 43aa6942..95987d16 100644 --- a/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java +++ b/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java @@ -10,8 +10,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.io.Serializable; - import static io.hexlet.typoreporter.test.factory.EntitiesFactory.ACCOUNT_INCORRECT_EMAIL; @Component diff --git a/src/test/java/io/hexlet/typoreporter/web/LoginIT.java b/src/test/java/io/hexlet/typoreporter/web/LoginIT.java index 29d387bc..1e9cdf85 100644 --- a/src/test/java/io/hexlet/typoreporter/web/LoginIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/LoginIT.java @@ -52,7 +52,7 @@ static void datasourceProperties(DynamicPropertyRegistry registry) { private static final String EMAIL_UPPER_CASE = "EMAIL_ADDRESS@GOOGLE.COM"; private static final String EMAIL_LOWER_CASE = EMAIL_UPPER_CASE.toLowerCase(); - private SignupAccountModel model = new SignupAccountModel( + private final SignupAccountModel model = new SignupAccountModel( "model_upper_case", EMAIL_UPPER_CASE, "password", "password", From cb8249f1b3bdd0088285e44aa5c3cee6d86d45a2 Mon Sep 17 00:00:00 2001 From: VictoryPashkova Date: Wed, 26 Jun 2024 16:49:27 +0600 Subject: [PATCH 05/71] feat: add new landing.html --- src/main/resources/templates/landing.html | 181 ++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/main/resources/templates/landing.html diff --git a/src/main/resources/templates/landing.html b/src/main/resources/templates/landing.html new file mode 100644 index 00000000..4e8411d2 --- /dev/null +++ b/src/main/resources/templates/landing.html @@ -0,0 +1,181 @@ + + + + + + FixIT + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+

FixIT

+

Бесплатный сервис для отправки сообщений об ошибках в тексте на Вашем сайте. После добавлении на ваш сайт виджета посетители имеют возможность выделить ошибку или опечатку и сообщить об этом администратору.

+
+ + +
+
+
+ fixit widget screen +
+
+
+
+
+
+

FixIT помогает сайтам

+
+
+
+ + + + +
+

Улучшить качество контента

+

Пользователи могут быстро сообщать об опечатках и ошибках, что способствует оперативному устранению неточностей и повышению качества текстов, улучшая восприятие информации и доверие к ресурсу.

+
+
+
+ + + + +
+

Эффективно управлять ошибками

+

Отслеживание статуса ошибок позволяет администраторам эффективно управлять процессом исправления и обновления контента, сокращая время, необходимое на обнаружение и устранение неточностей, что способствует оперативному обновлению сайта.

+
+
+
+ + + +
+

Повышать вовлеченность пользователей

+

Интеграция виджета способствует активному взаимодействию пользователей с администрацией сайта, делая их частью процесса улучшения ресурса. Это способствует формированию тесного и взаимовыгодного сообщества пользователей.

+
+
+
+
+
+
+

Как FixIT работает

+
+
+
+
+

Создайте свое рабочее пространство

+

Создайте аккаунт и добавляйте неограниченное количество сайтов.

+ Зарегистрироваться +
+
+
+
+
+
+

Добавьте виджет на сайт

+

Добавьте виджет на сайт, чтобы пользователи могли легко сообщать об ошибках.

+
+
+
+
+
+
+

Управляйте входящими сообщениями

+

Управляйте входящими сообщениями и эффективно устраняйте найденные опечатки.

+
+
+
+
+
+
+
+ + + From 064171cb2f542a356408af5e1e1d40c7ef64eb00 Mon Sep 17 00:00:00 2001 From: Borychev Andrey Date: Sun, 14 Jul 2024 19:49:52 +0300 Subject: [PATCH 06/71] [#260] fix by feedback of review --- build.gradle.kts | 3 - .../account/constraint/AccountUsername.java | 2 +- .../web/model/SignupAccountModel.java | 3 +- .../web/model/WorkspaceUserModel.java | 2 +- src/main/resources/messages_en.properties | 3 - src/main/resources/messages_ru.properties | 2 - .../typoreporter/config/TestConfig.java | 18 --- .../test/factory/AccountModelGenerator.java | 44 ------- .../typoreporter/utils/BundleSourceUtils.java | 75 ------------ .../hexlet/typoreporter/utils/ModelUtils.java | 32 ------ .../typoreporter/web/AccountControllerIT.java | 108 ++++++++---------- 11 files changed, 51 insertions(+), 241 deletions(-) delete mode 100644 src/test/java/io/hexlet/typoreporter/config/TestConfig.java delete mode 100644 src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java delete mode 100644 src/test/java/io/hexlet/typoreporter/utils/BundleSourceUtils.java delete mode 100644 src/test/java/io/hexlet/typoreporter/utils/ModelUtils.java diff --git a/build.gradle.kts b/build.gradle.kts index 10ef00f0..bbb573a4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,9 +48,6 @@ dependencies { implementation("org.mapstruct:mapstruct:1.5.3.Final") // Annotation processors annotationProcessor("org.mapstruct:mapstruct-processor:1.5.3.Final") - //Generating models for tests - implementation("org.instancio:instancio-junit:3.6.0") - implementation("net.datafaker:datafaker:2.0.2") // Testing testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.security:spring-security-test") diff --git a/src/main/java/io/hexlet/typoreporter/domain/account/constraint/AccountUsername.java b/src/main/java/io/hexlet/typoreporter/domain/account/constraint/AccountUsername.java index 370d3d02..7520cca1 100644 --- a/src/main/java/io/hexlet/typoreporter/domain/account/constraint/AccountUsername.java +++ b/src/main/java/io/hexlet/typoreporter/domain/account/constraint/AccountUsername.java @@ -13,7 +13,7 @@ import java.lang.annotation.Target; @NotBlank -@Pattern(regexp = "^[-_A-Za-z0-9]*$", message = "{validation.alert.wrong-username-pattern}") +@Pattern(regexp = "^[-_A-Za-z0-9]*$") @Size(min = 2, max = 20) @Constraint(validatedBy = {}) @Target({ElementType.FIELD, ElementType.PARAMETER}) diff --git a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java index 00d7a39e..b475a10b 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java @@ -27,8 +27,7 @@ public class SignupAccountModel { private String username; @Email(regexp = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", - //message = "The email \"${validatedValue}\" is not valid") - message = "{validation.alert.wrong-email}") + message = "The email \"${validatedValue}\" is not valid") private String email; @AccountPassword diff --git a/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java b/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java index 1b424db1..17d088a7 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/WorkspaceUserModel.java @@ -13,6 +13,6 @@ public class WorkspaceUserModel { @Email(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", - message = "{validation.alert.wrong-email}") + message = "The email \"${validatedValue}\" is not valid") private String email; } diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index eadf47ff..892b32df 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -104,6 +104,3 @@ btn.delete-from-wks=Delete from workspace alert.password-wrong-format=Password must be between 8 and 20 characters \ and contain only latin letters, digits and symbols ~`!@#$%^&*()_-+={[}]|\:;"'<,>.?/ - -validation.alert.wrong-email=The email "${validatedValue}" is not valid -validation.alert.wrong-username-pattern=The pattern of username must be "{regexp}" diff --git a/src/main/resources/messages_ru.properties b/src/main/resources/messages_ru.properties index d36b20fe..65312c97 100644 --- a/src/main/resources/messages_ru.properties +++ b/src/main/resources/messages_ru.properties @@ -102,5 +102,3 @@ text.wks-delete-confirm=Удалить пространство? alert.password-wrong-format=Пароль должен быть от 8 до 20 символов\ \ и содержать только буквы латинского алфавита,\ \ цифры и символы ~`!@#$%^&*()_-+={[}]|\:;"'<,>.?/ -validation.alert.wrong-email=Электронная почта "${validatedValue}" указана некорректно -validation.alert.wrong-username-pattern=Имя пользователя должен быть подходить под шаблон "{regexp}" diff --git a/src/test/java/io/hexlet/typoreporter/config/TestConfig.java b/src/test/java/io/hexlet/typoreporter/config/TestConfig.java deleted file mode 100644 index c9485f64..00000000 --- a/src/test/java/io/hexlet/typoreporter/config/TestConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.hexlet.typoreporter.config; - -import net.datafaker.Faker; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; - -@Configuration -public class TestConfig { - @Bean - public Faker getFaker() { - return new Faker(); - } - @Bean - public ObjectMapper getObjectMapper() { - return new ObjectMapper(); - } -} diff --git a/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java b/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java deleted file mode 100644 index 95987d16..00000000 --- a/src/test/java/io/hexlet/typoreporter/test/factory/AccountModelGenerator.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.hexlet.typoreporter.test.factory; - -import io.hexlet.typoreporter.web.model.SignupAccountModel; -import jakarta.annotation.PostConstruct; -import lombok.Getter; -import net.datafaker.Faker; -import org.instancio.Instancio; -import org.instancio.Model; -import org.instancio.Select; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import static io.hexlet.typoreporter.test.factory.EntitiesFactory.ACCOUNT_INCORRECT_EMAIL; - -@Component -@Getter -public class AccountModelGenerator { - private Model correctAccountModel; - private Model incorrectAccountModel; - @Autowired - private Faker faker; - - @PostConstruct - public void init() { - final String password = faker.internet().password(8, 20); - final String incorrectPassword = faker.internet().password(1, 7); - correctAccountModel = Instancio.of(SignupAccountModel.class) - .supply(Select.field(SignupAccountModel::getUsername), () -> faker.name().firstName()) - .supply(Select.field(SignupAccountModel::getEmail), () -> faker.internet().emailAddress()) - .supply(Select.field(SignupAccountModel::getPassword), () -> password) - .supply(Select.field(SignupAccountModel::getConfirmPassword), () -> password) - .supply(Select.field(SignupAccountModel::getFirstName), () -> faker.name().firstName()) - .supply(Select.field(SignupAccountModel::getLastName), () -> faker.name().lastName()) - .toModel(); - incorrectAccountModel = Instancio.of(SignupAccountModel.class) - .supply(Select.field(SignupAccountModel::getUsername), () -> faker.name().firstName() + ".") - .supply(Select.field(SignupAccountModel::getEmail), () -> ACCOUNT_INCORRECT_EMAIL) - .supply(Select.field(SignupAccountModel::getPassword), () -> incorrectPassword) - .supply(Select.field(SignupAccountModel::getConfirmPassword), () -> incorrectPassword) - .supply(Select.field(SignupAccountModel::getFirstName), () -> "") - .supply(Select.field(SignupAccountModel::getLastName), () -> "") - .toModel(); - } -} diff --git a/src/test/java/io/hexlet/typoreporter/utils/BundleSourceUtils.java b/src/test/java/io/hexlet/typoreporter/utils/BundleSourceUtils.java deleted file mode 100644 index bcbf0e50..00000000 --- a/src/test/java/io/hexlet/typoreporter/utils/BundleSourceUtils.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.hexlet.typoreporter.utils; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.MessageSource; -import org.springframework.stereotype.Component; - -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -@Component -public class BundleSourceUtils { - @Autowired - private MessageSource messageSource; - - /** - * @param key - Ключ из messages_LANG.properties, по которому необходимо получить значение - * Для RU необходимо использовать new Locale("ru") - * @param locale - Параметр, по которому определяется язык (локализация) - * @param isReplaceToQuot - Признак необходимости замены " на " - * @param args - Аргументы с типом String, которые необходимо подставить при интерполяции - * @return Строка из resource bundle изменениями - */ - public String getValueByKey(String key, Locale locale, boolean isReplaceToQuot, String[] args) { - String value = messageSource.getMessage(key, null, locale); - if (args != null) { - Pattern pattern = Pattern.compile("(\\$\\{(\\w+)\\})|(\\{(\\w+)\\})"); - Matcher matcher = pattern.matcher(value); - while (matcher.find()) { - value = value.replace(matcher.group(), "%s"); - } - value = isReplaceToQuot - ? value.replace("\"", """) - : value; - return String.format(value, args); - } - return isReplaceToQuot ? value.replace("\"", """) : value; - } - - /** - * @param key - Ключ из messages_LANG.properties, по которому необходимо получить значение - * @param isReplaceToQuot - Признак необходимости замены " на " - * @param args - Аргументы с типом String, которые необходимо подставить при интерполяции - * @return - Строка из resource bundle изменениями, по умолчанию на английском языке - */ - public String getValueByKey(String key, boolean isReplaceToQuot, String[] args) { - return getValueByKey(key, Locale.ENGLISH, isReplaceToQuot, args); - } - - /** - * @param key - Ключ из messages_LANGUAGE.properties, по которому необходимо получить значение. - * @param isReplaceToQuot - Признак необходимости замены " на " - * @return - Строка из resource bundle изменениями, по умолчанию на английском языке - */ - public String getValueByKey(String key, boolean isReplaceToQuot) { - return getValueByKey(key, isReplaceToQuot, null); - } - - /** - * @param key - Ключ из messages_LANG.properties, по которому необходимо получить значение - * @param args - Аргументы с типом String, которые необходимо подставить при интерполяции - * @return - Строка из resource bundle, по умолчанию на английском языке - */ - public String getValueByKey(String key, String[] args) { - return getValueByKey(key, false, args); - } - - /** - * @param key - Ключ из messages_LANG.properties, по которому необходимо получить значение - * @return - Строка из resource bundle, по умолчанию на английском языке - */ - public String getValueByKey(String key) { - return getValueByKey(key, null); - } -} diff --git a/src/test/java/io/hexlet/typoreporter/utils/ModelUtils.java b/src/test/java/io/hexlet/typoreporter/utils/ModelUtils.java deleted file mode 100644 index 98ee2042..00000000 --- a/src/test/java/io/hexlet/typoreporter/utils/ModelUtils.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.hexlet.typoreporter.utils; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectReader; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -import java.util.Map; -import java.util.Set; - -@Component -public class ModelUtils { - @Autowired - private ObjectMapper objectMapper; - - public MultiValueMap toFormParams(Object dto) throws Exception { - return toFormParams(dto, Set.of()); - } - - public MultiValueMap toFormParams(Object dto, Set excludeFields) throws Exception { - ObjectReader reader = objectMapper.readerFor(Map.class); - Map map = reader.readValue(objectMapper.writeValueAsString(dto)); - - MultiValueMap multiValueMap = new LinkedMultiValueMap<>(); - map.entrySet().stream() - .filter(e -> !excludeFields.contains(e.getKey())) - .forEach(e -> multiValueMap.add(e.getKey(), (e.getValue() == null ? "" : e.getValue()))); - return multiValueMap; - } -} diff --git a/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java index 2e91ee63..3603955c 100644 --- a/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/AccountControllerIT.java @@ -4,16 +4,10 @@ import com.github.database.rider.spring.api.DBRider; import io.hexlet.typoreporter.repository.AccountRepository; import io.hexlet.typoreporter.test.DBUnitEnumPostgres; -import io.hexlet.typoreporter.test.factory.AccountModelGenerator; -import io.hexlet.typoreporter.utils.BundleSourceUtils; -import io.hexlet.typoreporter.utils.ModelUtils; -import io.hexlet.typoreporter.web.model.SignupAccountModel; -import org.instancio.Instancio; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.web.servlet.MockMvc; @@ -29,7 +23,6 @@ import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureMockMvc @@ -57,69 +50,64 @@ static void dataSourceProperties(DynamicPropertyRegistry registry) { @Autowired private AccountRepository accountRepository; - @Autowired - private AccountModelGenerator accountGenerator; - - @Autowired - private ModelUtils modelUtils; - @Autowired - private BundleSourceUtils bundleSourceUtils; - @Test void updateAccountWithWrongEmailDomain() throws Exception { - SignupAccountModel correctAccount = Instancio.of(accountGenerator.getCorrectAccountModel()).create(); - var correctFormParams = modelUtils.toFormParams(correctAccount); + String userName = "testUser"; + String correctEmailDomain = "test@test.test"; + String password = "_Qwe1234"; mockMvc.perform(post("/signup") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .params(correctFormParams) - .with(csrf())) - .andExpect(status().isFound()); - assertThat(accountRepository.findAccountByEmail(correctAccount.getEmail())).isNotEmpty(); - assertThat(accountRepository.findAccountByEmail(correctAccount.getEmail()).orElseThrow().getEmail()) - .isEqualTo(correctAccount.getEmail()); - - SignupAccountModel incorrectAccount = Instancio.of(accountGenerator.getIncorrectAccountModel()).create(); - var incorrectFormParams = modelUtils.toFormParams(incorrectAccount); - var response = mockMvc.perform(put("/account/update") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .params(incorrectFormParams) - .with(user(correctAccount.getEmail())) - .with(csrf())) - .andExpect(status().isOk()) - .andReturn(); + .param("username", userName) + .param("email", correctEmailDomain) + .param("password", password) + .param("confirmPassword", password) + .param("firstName", userName) + .param("lastName", userName) + .with(csrf())); + assertThat(accountRepository.findAccountByEmail(correctEmailDomain)).isNotEmpty(); + assertThat(accountRepository.findAccountByEmail(correctEmailDomain).orElseThrow().getEmail()) + .isEqualTo(correctEmailDomain); - var body = response.getResponse().getContentAsString(); - assertThat(accountRepository.findAccountByEmail(incorrectAccount.getEmail())).isEmpty(); - assertThat(accountRepository.findAccountByEmail(correctAccount.getEmail()).orElseThrow().getEmail()) - .isEqualTo(correctAccount.getEmail()); - assertThat(body).contains(bundleSourceUtils.getValueByKey( - "validation.alert.wrong-email", true, new String[]{incorrectAccount.getEmail()})); - assertThat(body).contains(bundleSourceUtils.getValueByKey( - "validation.alert.wrong-username-pattern", true, new String[]{"^[-_A-Za-z0-9]*$"})); + String wrongEmailDomain = "test@test"; + mockMvc.perform(post("/account/update") + .param("username", userName) + .param("email", wrongEmailDomain) + .param("password", password) + .param("confirmPassword", password) + .param("firstName", userName) + .param("lastName", userName) + .with(user(correctEmailDomain)) + .with(csrf())); + assertThat(accountRepository.findAccountByEmail(wrongEmailDomain)).isEmpty(); + assertThat(accountRepository.findAccountByEmail(correctEmailDomain).orElseThrow().getEmail()) + .isEqualTo(correctEmailDomain); } @Test void updateAccountEmailUsingDifferentCase() throws Exception { - final SignupAccountModel account = Instancio.of(accountGenerator.getCorrectAccountModel()).create(); - account.setEmail(account.getEmail().toUpperCase()); - var accountFormParams = modelUtils.toFormParams(account); + final String username = "testUser"; + final String emailUpperCase = "TEST@TEST.RU"; + final String emailMixedCase = "TEST@test.Ru"; + final String emailLowerCase = "test@test.ru"; + final String password = "_Qwe1234"; + mockMvc.perform(post("/signup") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .params(accountFormParams) - .with(csrf())) - .andExpect(status().isFound()); - assertThat(accountRepository.findAccountByEmail(account.getEmail().toLowerCase())).isNotEmpty(); + .param("username", username) + .param("email", emailMixedCase) + .param("password", password) + .param("confirmPassword", password) + .param("firstName", username) + .param("lastName", username) + .with(csrf())); + assertThat(accountRepository.findAccountByEmail(emailLowerCase)).isNotEmpty(); - final SignupAccountModel accountToUpdate = Instancio.of(accountGenerator.getCorrectAccountModel()).create(); - accountToUpdate.setEmail(account.getEmail().toLowerCase()); - accountFormParams = modelUtils.toFormParams(accountToUpdate); mockMvc.perform(put("/account/update") - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .params(accountFormParams) - .with(user(account.getEmail())) - .with(csrf())) - .andExpect(status().isOk()); - assertThat(accountRepository.findAccountByEmail(accountToUpdate.getEmail().toUpperCase())).isEmpty(); - assertThat(accountRepository.findAccountByEmail(accountToUpdate.getEmail())).isNotEmpty(); + .param("firstName", username) + .param("lastName", username) + .param("username", username) + .param("email", emailUpperCase) + .with(user(emailLowerCase)) + .with(csrf())); + assertThat(accountRepository.findAccountByEmail(emailUpperCase)).isEmpty(); + assertThat(accountRepository.findAccountByEmail(emailLowerCase)).isNotEmpty(); } } From 517ee53c9f0a1c1ace15fcf6a65e28e756eb0869 Mon Sep 17 00:00:00 2001 From: naumenko-tanya Date: Fri, 26 Jul 2024 09:30:16 +0300 Subject: [PATCH 07/71] Delete non-admin user deletion button in workspace --- .../templates/workspace/wks-users.html | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/main/resources/templates/workspace/wks-users.html b/src/main/resources/templates/workspace/wks-users.html index b7023c12..642f6b0b 100644 --- a/src/main/resources/templates/workspace/wks-users.html +++ b/src/main/resources/templates/workspace/wks-users.html @@ -103,7 +103,7 @@ - + @@ -121,24 +121,8 @@ - - -
-
-
- -
- - - -
-
-
-
- - + +
From 154196d84abaa9e3927d3832dc22fdacb3bf0838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D0=BD=D0=B0=20=D0=91=D0=B5=D0=BB?= =?UTF-8?q?=D1=8F=D0=BA=D0=B0=D0=B5=D0=B2=D0=B0?= Date: Thu, 1 Aug 2024 17:36:33 +0300 Subject: [PATCH 08/71] rework the script connection #283 --- .../templates/workspace/wks-integration.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/resources/templates/workspace/wks-integration.html b/src/main/resources/templates/workspace/wks-integration.html index f0d431b2..22e7817a 100644 --- a/src/main/resources/templates/workspace/wks-integration.html +++ b/src/main/resources/templates/workspace/wks-integration.html @@ -6,13 +6,19 @@
-
+

-<script src="https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@main/src/widget/index.js"></script>;
-
+<script>
+document.addEventListener('DOMContentLoaded', function () {
+    const script = document.createElement('script');
+    script.src = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@main/src/widget/index.js?v=' + new Date().getTime();
+    document.body.appendChild(script);
+});
+</script>;
+  
 <script>
     handleTypoReporter({ authorizationToken: '[[${wksBasicToken}]]',
     workSpaceUrl: '[[${rootUrl}]]', workSpaceId: '[[${wksId}]]'})

From a6d050ea952b3b2f5b9498f673426bb70b2241d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D0=BD=D0=B0=20=D0=91=D0=B5=D0=BB?=
 =?UTF-8?q?=D1=8F=D0=BA=D0=B0=D0=B5=D0=B2=D0=B0?=
 
Date: Thu, 1 Aug 2024 18:50:24 +0300
Subject: [PATCH 09/71] add console.log to widget test #283

---
 src/main/resources/templates/workspace/wks-integration.html | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/main/resources/templates/workspace/wks-integration.html b/src/main/resources/templates/workspace/wks-integration.html
index 22e7817a..2e1df302 100644
--- a/src/main/resources/templates/workspace/wks-integration.html
+++ b/src/main/resources/templates/workspace/wks-integration.html
@@ -16,9 +16,10 @@
     const script = document.createElement('script');
     script.src = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@main/src/widget/index.js?v=' + new Date().getTime();
     document.body.appendChild(script);
+
+    console.log("d495bd24a1e9db08f68b63d5fbc238c4e143931c");
 });
 </script>;
-  
 <script>
     handleTypoReporter({ authorizationToken: '[[${wksBasicToken}]]',
     workSpaceUrl: '[[${rootUrl}]]', workSpaceId: '[[${wksId}]]'})

From 5b99a17fc2fac859f2ca431c2db495fd9e56d546 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D0=BD=D0=B0=20=D0=91=D0=B5=D0=BB?=
 =?UTF-8?q?=D1=8F=D0=BA=D0=B0=D0=B5=D0=B2=D0=B0?=
 
Date: Thu, 1 Aug 2024 20:43:12 +0300
Subject: [PATCH 10/71] fix console.log for test

---
 .../templates/workspace/wks-integration.html  |   2 -
 src/widget/index.html                         |  10 +-
 src/widget/index.js                           | 234 ++++++++++--------
 3 files changed, 142 insertions(+), 104 deletions(-)

diff --git a/src/main/resources/templates/workspace/wks-integration.html b/src/main/resources/templates/workspace/wks-integration.html
index 2e1df302..e7cc6626 100644
--- a/src/main/resources/templates/workspace/wks-integration.html
+++ b/src/main/resources/templates/workspace/wks-integration.html
@@ -16,8 +16,6 @@
     const script = document.createElement('script');
     script.src = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@main/src/widget/index.js?v=' + new Date().getTime();
     document.body.appendChild(script);
-
-    console.log("d495bd24a1e9db08f68b63d5fbc238c4e143931c");
 });
 </script>;
 <script>
diff --git a/src/widget/index.html b/src/widget/index.html
index c2757d69..955c26d2 100644
--- a/src/widget/index.html
+++ b/src/widget/index.html
@@ -217,10 +217,16 @@ 

Guides

- + ; + diff --git a/src/widget/index.js b/src/widget/index.js index 451858e2..ab1d3a67 100644 --- a/src/widget/index.js +++ b/src/widget/index.js @@ -1,5 +1,7 @@ +console.log("d495bd24a1e9db08f68b63d5fbc238c4e143931c"); + const generateModalStyles = () => { - const modalStyleEl = document.createElement('style'); + const modalStyleEl = document.createElement("style"); modalStyleEl.textContent = ` #hexlet-correction-modal_modal { position: fixed; @@ -114,72 +116,81 @@ const generateModalStyles = () => { const handleBodyScroll = (modalIsOpen) => { if (modalIsOpen) { - document.body.style.overflow = 'hidden'; + document.body.style.overflow = "hidden"; } else { - document.body.style.overflow = 'auto'; + document.body.style.overflow = "auto"; } }; const generateModal = (state) => { generateModalStyles(); - const modalEl = document.createElement('div'); - modalEl.id = 'hexlet-correction-modal_modal'; - modalEl.style.display = state.modalShown ? 'block' : 'none'; + const modalEl = document.createElement("div"); + modalEl.id = "hexlet-correction-modal_modal"; + modalEl.style.display = state.modalShown ? "block" : "none"; - const divContent = document.createElement('div'); - divContent.id = 'hexlet-correction-modal_modal-content'; + const divContent = document.createElement("div"); + divContent.id = "hexlet-correction-modal_modal-content"; modalEl.append(divContent); - const divTypoReporter = document.createElement('div'); - divTypoReporter.id = 'hexlet-correction-modal_ReportTypo'; + const divTypoReporter = document.createElement("div"); + divTypoReporter.id = "hexlet-correction-modal_ReportTypo"; divContent.append(divTypoReporter); - const divHeader = document.createElement('div'); - divHeader.id = 'hexlet-correction-modal_ReportTypo-header'; - divHeader.textContent = 'Сообщите об ошибке на странице'; - - const divFirstLabel = document.createElement('div'); - divFirstLabel.classList.add('hexlet-correction-modal_ReportTypo-label'); - divFirstLabel.textContent = 'Ошибка содержится в следующем тексте:'; - - const selectedTextEl = document.createElement('div'); - selectedTextEl.id = 'hexlet-correction-modal_ReportTypo-message'; - - const inputName = document.createElement('input'); - inputName.type = 'text'; - inputName.placeholder = 'Введите свое имя или email'; - inputName.id = 'hexlet-correction-modal_ReportTypo-name'; - inputName.value = state.options.userName === null ? '' : state.options.userName; - - const commentEl = document.createElement('textarea'); - commentEl.id = 'hexlet-correction-modal_ReportTypo-comment'; - commentEl.placeholder = 'Опишите ошибку'; - - const divSecondLabel = document.createElement('div'); - divSecondLabel.classList.add('hexlet-correction-modal_ReportTypo-label'); - const strong = document.createElement('strong'); - strong.id = 'hexlet-correction-modal_question'; - strong.textContent = 'Отправить сообщение об ошибке редактору сайта?'; + const divHeader = document.createElement("div"); + divHeader.id = "hexlet-correction-modal_ReportTypo-header"; + divHeader.textContent = "Сообщите об ошибке на странице"; + + const divFirstLabel = document.createElement("div"); + divFirstLabel.classList.add("hexlet-correction-modal_ReportTypo-label"); + divFirstLabel.textContent = "Ошибка содержится в следующем тексте:"; + + const selectedTextEl = document.createElement("div"); + selectedTextEl.id = "hexlet-correction-modal_ReportTypo-message"; + + const inputName = document.createElement("input"); + inputName.type = "text"; + inputName.placeholder = "Введите свое имя или email"; + inputName.id = "hexlet-correction-modal_ReportTypo-name"; + inputName.value = + state.options.userName === null ? "" : state.options.userName; + + const commentEl = document.createElement("textarea"); + commentEl.id = "hexlet-correction-modal_ReportTypo-comment"; + commentEl.placeholder = "Опишите ошибку"; + + const divSecondLabel = document.createElement("div"); + divSecondLabel.classList.add("hexlet-correction-modal_ReportTypo-label"); + const strong = document.createElement("strong"); + strong.id = "hexlet-correction-modal_question"; + strong.textContent = "Отправить сообщение об ошибке редактору сайта?"; divSecondLabel.append(strong); - const divButtons = document.createElement('div'); - divButtons.style.textAlign = 'right'; + const divButtons = document.createElement("div"); + divButtons.style.textAlign = "right"; - const submitBtn = document.createElement('button'); - submitBtn.type = 'button'; - submitBtn.id = 'hexlet-correction-modal_ReportTypo-submit'; - submitBtn.textContent = 'Отправить'; + const submitBtn = document.createElement("button"); + submitBtn.type = "button"; + submitBtn.id = "hexlet-correction-modal_ReportTypo-submit"; + submitBtn.textContent = "Отправить"; - const cancelBtn = document.createElement('button'); - cancelBtn.type = 'button'; - cancelBtn.id = 'hexlet-correction-modal_ReportTypo-cancel'; - cancelBtn.textContent = 'Отмена'; + const cancelBtn = document.createElement("button"); + cancelBtn.type = "button"; + cancelBtn.id = "hexlet-correction-modal_ReportTypo-cancel"; + cancelBtn.textContent = "Отмена"; divButtons.append(submitBtn, cancelBtn); - divTypoReporter.append(divHeader, divFirstLabel, selectedTextEl, inputName, commentEl, divSecondLabel, divButtons); + divTypoReporter.append( + divHeader, + divFirstLabel, + selectedTextEl, + inputName, + commentEl, + divSecondLabel, + divButtons + ); - const body = document.querySelector('body'); + const body = document.querySelector("body"); body.append(modalEl); return { @@ -194,61 +205,68 @@ const generateModal = (state) => { const resetModalState = (state) => { state.modalShown = false; - state.data.reporterComment = ''; - state.data.textBeforeTypo = ''; - state.data.textTypo = ''; - state.data.textAfterTypo = ''; + state.data.reporterComment = ""; + state.data.textBeforeTypo = ""; + state.data.textTypo = ""; + state.data.textAfterTypo = ""; }; const renderModal = (elements, state) => { if (state.modalShown) { - elements.modalEl.style.display = 'block'; + elements.modalEl.style.display = "block"; const { textBeforeTypo, textTypo, textAfterTypo } = state.data; elements.selectedTextEl.innerHTML = `${textBeforeTypo}${textTypo}${textAfterTypo}`; - elements.inputName.value = state.data.reporterName !== '' ? state.data.reporterName : state.options.userName; + elements.inputName.value = + state.data.reporterName !== "" + ? state.data.reporterName + : state.options.userName; elements.commentEl.value = state.data.reporterComment; elements.commentEl.focus(); return; } - elements.modalEl.style.display = 'none'; - elements.selectedTextEl.innerHTML = ''; - elements.commentEl.value = ''; + elements.modalEl.style.display = "none"; + elements.selectedTextEl.innerHTML = ""; + elements.commentEl.value = ""; }; const sendData = (elements, state) => async (event) => { event.preventDefault(); const { value } = elements.inputName; - state.data.reporterName = value === '' ? 'Anonymous' : value; + state.data.reporterName = value === "" ? "Anonymous" : value; state.data.reporterComment = elements.commentEl.value; try { - await fetch(`${state.options.workSpaceUrl}/api/workspaces/${state.options.workSpaceId}/typos`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Basic ${state.options.authorizationToken}`, - }, - body: JSON.stringify(state.data), - }); + await fetch( + `${state.options.workSpaceUrl}/api/workspaces/${state.options.workSpaceId}/typos`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Basic ${state.options.authorizationToken}`, + }, + body: JSON.stringify(state.data), + } + ); resetModalState(state); } catch (error) { - throw new Error('Произошла ошибка:', error); + throw new Error("Произошла ошибка:", error); } }; const view = (elements, state) => { - const watch = (state, callback) => new Proxy(state, { - set(target, prop, value) { - const prevValue = target[prop]; - const result = Reflect.set(target, prop, value); - callback(prop, value, prevValue); - return result; - }, - }); + const watch = (state, callback) => + new Proxy(state, { + set(target, prop, value) { + const prevValue = target[prop]; + const result = Reflect.set(target, prop, value); + callback(prop, value, prevValue); + return result; + }, + }); const watchedState = watch(state, (path) => { switch (path) { - case 'modalShown': + case "modalShown": renderModal(elements, state); handleBodyScroll(state.modalShown); break; @@ -258,46 +276,50 @@ const view = (elements, state) => { }); return watchedState; -} +}; const isSelectionLeftToRight = (selection) => { const range = document.createRange(); range.setStart(selection.focusNode, selection.focusOffset); range.setEnd(selection.anchorNode, selection.anchorOffset); return range.collapsed; -} +}; const handleTypoReporter = (options) => { - if (!options || !options.authorizationToken && !options.workSpaceId) { - throw new Error('Для работы модуля требуется указать workSpaceId и authorizationToken'); + if (!options || (!options.authorizationToken && !options.workSpaceId)) { + throw new Error( + "Для работы модуля требуется указать workSpaceId и authorizationToken" + ); } const initialState = { modalShown: false, options: { - workSpaceUrl: 'https://hexlet-correction.herokuapp.com/api/workspaces', + workSpaceUrl: "https://hexlet-correction.herokuapp.com/api/workspaces", userName: null, ...options, }, data: { - pageUrl: '', - reporterName: '', - reporterComment: '', - textBeforeTypo: '', - textTypo: '', - textAfterTypo: '', + pageUrl: "", + reporterName: "", + reporterComment: "", + textBeforeTypo: "", + textTypo: "", + textAfterTypo: "", }, }; const elements = generateModal(initialState); const state = view(elements, initialState); - document.addEventListener('keydown', (event) => { + console.log("Widget is initialized"); + + document.addEventListener("keydown", (event) => { const selection = window.getSelection(); if (selection.isCollapsed || state.modalShown) { return; } - if (event.ctrlKey && event.key === 'Enter') { + if (event.ctrlKey && event.key === "Enter") { state.data.pageUrl = window.location.href; const { anchorNode } = selection; @@ -307,33 +329,45 @@ const handleTypoReporter = (options) => { const maxLength = 50; state.data.textTypo = selection.toString(); - if(isSelectionLeftToRight(selection)) { + if (isSelectionLeftToRight(selection)) { const start = Math.max(anchorOffset - maxLength, 0); const end = Math.min(focusOffset + maxLength, anchorNode.length); - state.data.textBeforeTypo = anchorNode.textContent.substring(start, anchorOffset); - state.data.textAfterTypo = anchorNode.substringData(focusOffset, end - focusOffset); + state.data.textBeforeTypo = anchorNode.textContent.substring( + start, + anchorOffset + ); + state.data.textAfterTypo = anchorNode.substringData( + focusOffset, + end - focusOffset + ); } else { - const start = Math.max(focusOffset - maxLength, 0); + const start = Math.max(focusOffset - maxLength, 0); const end = Math.min(anchorOffset + maxLength, anchorNode.length); - state.data.textBeforeTypo = anchorNode.textContent.substring(start, focusOffset); - state.data.textAfterTypo = anchorNode.substringData(anchorOffset, end - anchorOffset); + state.data.textBeforeTypo = anchorNode.textContent.substring( + start, + focusOffset + ); + state.data.textAfterTypo = anchorNode.substringData( + anchorOffset, + end - anchorOffset + ); } state.modalShown = true; } }); - elements.submitBtn.addEventListener('click', sendData(elements, state)); - elements.cancelBtn.addEventListener('click', () => resetModalState(state)); + elements.submitBtn.addEventListener("click", sendData(elements, state)); + elements.cancelBtn.addEventListener("click", () => resetModalState(state)); - elements.modalEl.addEventListener('click', (event) => { + elements.modalEl.addEventListener("click", (event) => { if (event.target === elements.modalEl) { resetModalState(state); } }); - elements.modalEl.addEventListener('keydown', (event) => { - if (state.modalShown && event.key === 'Escape') { + elements.modalEl.addEventListener("keydown", (event) => { + if (state.modalShown && event.key === "Escape") { resetModalState(state); } }); From ebe392dc9b0769da74e771af664db85133423117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D0=BD=D0=B0=20=D0=91=D0=B5=D0=BB?= =?UTF-8?q?=D1=8F=D0=BA=D0=B0=D0=B5=D0=B2=D0=B0?= Date: Fri, 2 Aug 2024 15:43:41 +0300 Subject: [PATCH 11/71] fix console.log & quotes --- src/widget/index.js | 211 +++++++++++++++++++------------------------- 1 file changed, 89 insertions(+), 122 deletions(-) diff --git a/src/widget/index.js b/src/widget/index.js index ab1d3a67..c189ce69 100644 --- a/src/widget/index.js +++ b/src/widget/index.js @@ -1,7 +1,5 @@ -console.log("d495bd24a1e9db08f68b63d5fbc238c4e143931c"); - const generateModalStyles = () => { - const modalStyleEl = document.createElement("style"); + const modalStyleEl = document.createElement('style'); modalStyleEl.textContent = ` #hexlet-correction-modal_modal { position: fixed; @@ -116,81 +114,72 @@ const generateModalStyles = () => { const handleBodyScroll = (modalIsOpen) => { if (modalIsOpen) { - document.body.style.overflow = "hidden"; + document.body.style.overflow = 'hidden'; } else { - document.body.style.overflow = "auto"; + document.body.style.overflow = 'auto'; } }; const generateModal = (state) => { generateModalStyles(); - const modalEl = document.createElement("div"); - modalEl.id = "hexlet-correction-modal_modal"; - modalEl.style.display = state.modalShown ? "block" : "none"; + const modalEl = document.createElement('div'); + modalEl.id = 'hexlet-correction-modal_modal'; + modalEl.style.display = state.modalShown ? 'block' : 'none'; - const divContent = document.createElement("div"); - divContent.id = "hexlet-correction-modal_modal-content"; + const divContent = document.createElement('div'); + divContent.id = 'hexlet-correction-modal_modal-content'; modalEl.append(divContent); - const divTypoReporter = document.createElement("div"); - divTypoReporter.id = "hexlet-correction-modal_ReportTypo"; + const divTypoReporter = document.createElement('div'); + divTypoReporter.id = 'hexlet-correction-modal_ReportTypo'; divContent.append(divTypoReporter); - const divHeader = document.createElement("div"); - divHeader.id = "hexlet-correction-modal_ReportTypo-header"; - divHeader.textContent = "Сообщите об ошибке на странице"; - - const divFirstLabel = document.createElement("div"); - divFirstLabel.classList.add("hexlet-correction-modal_ReportTypo-label"); - divFirstLabel.textContent = "Ошибка содержится в следующем тексте:"; - - const selectedTextEl = document.createElement("div"); - selectedTextEl.id = "hexlet-correction-modal_ReportTypo-message"; - - const inputName = document.createElement("input"); - inputName.type = "text"; - inputName.placeholder = "Введите свое имя или email"; - inputName.id = "hexlet-correction-modal_ReportTypo-name"; - inputName.value = - state.options.userName === null ? "" : state.options.userName; - - const commentEl = document.createElement("textarea"); - commentEl.id = "hexlet-correction-modal_ReportTypo-comment"; - commentEl.placeholder = "Опишите ошибку"; - - const divSecondLabel = document.createElement("div"); - divSecondLabel.classList.add("hexlet-correction-modal_ReportTypo-label"); - const strong = document.createElement("strong"); - strong.id = "hexlet-correction-modal_question"; - strong.textContent = "Отправить сообщение об ошибке редактору сайта?"; + const divHeader = document.createElement('div'); + divHeader.id = 'hexlet-correction-modal_ReportTypo-header'; + divHeader.textContent = 'Сообщите об ошибке на странице'; + + const divFirstLabel = document.createElement('div'); + divFirstLabel.classList.add('hexlet-correction-modal_ReportTypo-label'); + divFirstLabel.textContent = 'Ошибка содержится в следующем тексте:'; + + const selectedTextEl = document.createElement('div'); + selectedTextEl.id = 'hexlet-correction-modal_ReportTypo-message'; + + const inputName = document.createElement('input'); + inputName.type = 'text'; + inputName.placeholder = 'Введите свое имя или email'; + inputName.id = 'hexlet-correction-modal_ReportTypo-name'; + inputName.value = state.options.userName === null ? '' : state.options.userName; + + const commentEl = document.createElement('textarea'); + commentEl.id = 'hexlet-correction-modal_ReportTypo-comment'; + commentEl.placeholder = 'Опишите ошибку'; + + const divSecondLabel = document.createElement('div'); + divSecondLabel.classList.add('hexlet-correction-modal_ReportTypo-label'); + const strong = document.createElement('strong'); + strong.id = 'hexlet-correction-modal_question'; + strong.textContent = 'Отправить сообщение об ошибке редактору сайта?'; divSecondLabel.append(strong); - const divButtons = document.createElement("div"); - divButtons.style.textAlign = "right"; + const divButtons = document.createElement('div'); + divButtons.style.textAlign = 'right'; - const submitBtn = document.createElement("button"); - submitBtn.type = "button"; - submitBtn.id = "hexlet-correction-modal_ReportTypo-submit"; - submitBtn.textContent = "Отправить"; + const submitBtn = document.createElement('button'); + submitBtn.type = 'button'; + submitBtn.id = 'hexlet-correction-modal_ReportTypo-submit'; + submitBtn.textContent = 'Отправить'; - const cancelBtn = document.createElement("button"); - cancelBtn.type = "button"; - cancelBtn.id = "hexlet-correction-modal_ReportTypo-cancel"; - cancelBtn.textContent = "Отмена"; + const cancelBtn = document.createElement('button'); + cancelBtn.type = 'button'; + cancelBtn.id = 'hexlet-correction-modal_ReportTypo-cancel'; + cancelBtn.textContent = 'Отмена'; divButtons.append(submitBtn, cancelBtn); - divTypoReporter.append( - divHeader, - divFirstLabel, - selectedTextEl, - inputName, - commentEl, - divSecondLabel, - divButtons - ); + divTypoReporter.append(divHeader, divFirstLabel, selectedTextEl, inputName, commentEl, divSecondLabel, divButtons); - const body = document.querySelector("body"); + const body = document.querySelector('body'); body.append(modalEl); return { @@ -205,57 +194,50 @@ const generateModal = (state) => { const resetModalState = (state) => { state.modalShown = false; - state.data.reporterComment = ""; - state.data.textBeforeTypo = ""; - state.data.textTypo = ""; - state.data.textAfterTypo = ""; + state.data.reporterComment = ''; + state.data.textBeforeTypo = ''; + state.data.textTypo = ''; + state.data.textAfterTypo = ''; }; const renderModal = (elements, state) => { if (state.modalShown) { - elements.modalEl.style.display = "block"; + elements.modalEl.style.display = 'block'; const { textBeforeTypo, textTypo, textAfterTypo } = state.data; elements.selectedTextEl.innerHTML = `${textBeforeTypo}${textTypo}${textAfterTypo}`; - elements.inputName.value = - state.data.reporterName !== "" - ? state.data.reporterName - : state.options.userName; + elements.inputName.value = state.data.reporterName !== '' ? state.data.reporterName : state.options.userName; elements.commentEl.value = state.data.reporterComment; elements.commentEl.focus(); return; } - elements.modalEl.style.display = "none"; - elements.selectedTextEl.innerHTML = ""; - elements.commentEl.value = ""; + elements.modalEl.style.display = 'none'; + elements.selectedTextEl.innerHTML = ''; + elements.commentEl.value = ''; }; const sendData = (elements, state) => async (event) => { event.preventDefault(); const { value } = elements.inputName; - state.data.reporterName = value === "" ? "Anonymous" : value; + state.data.reporterName = value === '' ? 'Anonymous' : value; state.data.reporterComment = elements.commentEl.value; try { - await fetch( - `${state.options.workSpaceUrl}/api/workspaces/${state.options.workSpaceId}/typos`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Basic ${state.options.authorizationToken}`, - }, - body: JSON.stringify(state.data), - } - ); + await fetch(`${state.options.workSpaceUrl}/api/workspaces/${state.options.workSpaceId}/typos`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Basic ${state.options.authorizationToken}`, + }, + body: JSON.stringify(state.data), + }); resetModalState(state); } catch (error) { - throw new Error("Произошла ошибка:", error); + throw new Error('Произошла ошибка:', error); } }; const view = (elements, state) => { - const watch = (state, callback) => - new Proxy(state, { + const watch = (state, callback) => new Proxy(state, { set(target, prop, value) { const prevValue = target[prop]; const result = Reflect.set(target, prop, value); @@ -266,7 +248,7 @@ const view = (elements, state) => { const watchedState = watch(state, (path) => { switch (path) { - case "modalShown": + case 'modalShown': renderModal(elements, state); handleBodyScroll(state.modalShown); break; @@ -286,40 +268,37 @@ const isSelectionLeftToRight = (selection) => { }; const handleTypoReporter = (options) => { + console.log('a6d050ea952b3b2f5b9498f673426bb70b2241d4'); if (!options || (!options.authorizationToken && !options.workSpaceId)) { - throw new Error( - "Для работы модуля требуется указать workSpaceId и authorizationToken" - ); + throw new Error('Для работы модуля требуется указать workSpaceId и authorizationToken'); } const initialState = { modalShown: false, options: { - workSpaceUrl: "https://hexlet-correction.herokuapp.com/api/workspaces", + workSpaceUrl: 'https://hexlet-correction.herokuapp.com/api/workspaces', userName: null, ...options, }, data: { - pageUrl: "", - reporterName: "", - reporterComment: "", - textBeforeTypo: "", - textTypo: "", - textAfterTypo: "", + pageUrl: '', + reporterName: '', + reporterComment: '', + textBeforeTypo: '', + textTypo: '', + textAfterTypo: '', }, }; const elements = generateModal(initialState); const state = view(elements, initialState); - console.log("Widget is initialized"); - - document.addEventListener("keydown", (event) => { + document.addEventListener('keydown', (event) => { const selection = window.getSelection(); if (selection.isCollapsed || state.modalShown) { return; } - if (event.ctrlKey && event.key === "Enter") { + if (event.ctrlKey && event.key === 'Enter') { state.data.pageUrl = window.location.href; const { anchorNode } = selection; @@ -332,42 +311,30 @@ const handleTypoReporter = (options) => { if (isSelectionLeftToRight(selection)) { const start = Math.max(anchorOffset - maxLength, 0); const end = Math.min(focusOffset + maxLength, anchorNode.length); - state.data.textBeforeTypo = anchorNode.textContent.substring( - start, - anchorOffset - ); - state.data.textAfterTypo = anchorNode.substringData( - focusOffset, - end - focusOffset - ); + state.data.textBeforeTypo = anchorNode.textContent.substring(start, anchorOffset); + state.data.textAfterTypo = anchorNode.substringData(focusOffset, end - focusOffset); } else { const start = Math.max(focusOffset - maxLength, 0); const end = Math.min(anchorOffset + maxLength, anchorNode.length); - state.data.textBeforeTypo = anchorNode.textContent.substring( - start, - focusOffset - ); - state.data.textAfterTypo = anchorNode.substringData( - anchorOffset, - end - anchorOffset - ); + state.data.textBeforeTypo = anchorNode.textContent.substring(start, focusOffset); + state.data.textAfterTypo = anchorNode.substringData(anchorOffset, end - anchorOffset); } state.modalShown = true; } }); - elements.submitBtn.addEventListener("click", sendData(elements, state)); - elements.cancelBtn.addEventListener("click", () => resetModalState(state)); + elements.submitBtn.addEventListener('click', sendData(elements, state)); + elements.cancelBtn.addEventListener('click', () => resetModalState(state)); - elements.modalEl.addEventListener("click", (event) => { + elements.modalEl.addEventListener('click', (event) => { if (event.target === elements.modalEl) { resetModalState(state); } }); - elements.modalEl.addEventListener("keydown", (event) => { - if (state.modalShown && event.key === "Escape") { + elements.modalEl.addEventListener('keydown', (event) => { + if (state.modalShown && event.key === 'Escape') { resetModalState(state); } }); From 13ccb4b8645520f3a4eeaae438ddc450085d3ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D0=BD=D0=B0=20=D0=91=D0=B5=D0=BB?= =?UTF-8?q?=D1=8F=D0=BA=D0=B0=D0=B5=D0=B2=D0=B0?= Date: Fri, 2 Aug 2024 16:05:25 +0300 Subject: [PATCH 12/71] remove new script from index.html --- src/widget/index.html | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/widget/index.html b/src/widget/index.html index 955c26d2..c2757d69 100644 --- a/src/widget/index.html +++ b/src/widget/index.html @@ -217,16 +217,10 @@

Guides

- ; + + From c09abb4553c41bbe54dcbd3d2bac3b1a9ef9d437 Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Fri, 2 Aug 2024 20:21:29 +0500 Subject: [PATCH 13/71] Update index.html --- src/widget/index.html | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/widget/index.html b/src/widget/index.html index c2757d69..545cf090 100644 --- a/src/widget/index.html +++ b/src/widget/index.html @@ -217,10 +217,16 @@

Guides

- - +; + From 73f615078a2936ba4488c5e2da3dab3c574dc02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D0=BD=D0=B0=20=D0=91=D0=B5=D0=BB?= =?UTF-8?q?=D1=8F=D0=BA=D0=B0=D0=B5=D0=B2=D0=B0?= Date: Mon, 5 Aug 2024 17:42:24 +0300 Subject: [PATCH 14/71] update rework the script connection - use greedy loading #283 --- .../templates/workspace/wks-integration.html | 29 ++++++++++++------- src/widget/index.js | 2 +- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/main/resources/templates/workspace/wks-integration.html b/src/main/resources/templates/workspace/wks-integration.html index e7cc6626..0d070878 100644 --- a/src/main/resources/templates/workspace/wks-integration.html +++ b/src/main/resources/templates/workspace/wks-integration.html @@ -12,18 +12,27 @@

 <script>
+function loadScript(src, callback) {
+  const body = document.querySelector('body')
+  const script = document.createElement('script');
+  script.src = src;
+  script.onload = callback;
+  body.appendChild(script);
+};
+
 document.addEventListener('DOMContentLoaded', function () {
-    const script = document.createElement('script');
-    script.src = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@main/src/widget/index.js?v=' + new Date().getTime();
-    document.body.appendChild(script);
+  const scriptSrc = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@main/src/widget/index.js?v=' + new Date().getTime();
+  loadScript(scriptSrc, function () {
+    handleTypoReporter({ 
+      authorizationToken: '[[${wksBasicToken}]]',
+      workSpaceUrl: '[[${rootUrl}]]', 
+      workSpaceId: '[[${wksId}]]'})
+  });
 });
-</script>;
-<script>
-    handleTypoReporter({ authorizationToken: '[[${wksBasicToken}]]',
-    workSpaceUrl: '[[${rootUrl}]]', workSpaceId: '[[${wksId}]]'})
-</script>
-
-
+</script> + +
+
diff --git a/src/widget/index.js b/src/widget/index.js index c189ce69..61e22abc 100644 --- a/src/widget/index.js +++ b/src/widget/index.js @@ -268,7 +268,7 @@ const isSelectionLeftToRight = (selection) => { }; const handleTypoReporter = (options) => { - console.log('a6d050ea952b3b2f5b9498f673426bb70b2241d4'); + console.log('Script loaded:', new Date().getTime()); if (!options || (!options.authorizationToken && !options.workSpaceId)) { throw new Error('Для работы модуля требуется указать workSpaceId и authorizationToken'); } From 75fa24315496bb0030ab8d085bab50fde475646f Mon Sep 17 00:00:00 2001 From: uchitsa Date: Wed, 7 Aug 2024 20:19:38 +0300 Subject: [PATCH 15/71] update signup account model for empty names --- .../typoreporter/web/model/SignupAccountModel.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java index b475a10b..32f0a224 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java @@ -4,13 +4,8 @@ import io.hexlet.typoreporter.domain.account.constraint.AccountUsername; import io.hexlet.typoreporter.service.dto.FieldMatchConsiderCase; import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; +import lombok.*; @Getter @Setter @@ -38,11 +33,9 @@ public class SignupAccountModel { @ToString.Exclude private String confirmPassword; - @NotBlank @Size(min = 1, max = 50) private String firstName; - @NotBlank @Size(min = 1, max = 50) private String lastName; } From 813a17e3e15d0d7adde1e1875a9034de5ee6e212 Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Thu, 8 Aug 2024 23:18:11 +0500 Subject: [PATCH 16/71] fix demo --- src/widget/index.html | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/widget/index.html b/src/widget/index.html index 545cf090..29ee1ed6 100644 --- a/src/widget/index.html +++ b/src/widget/index.html @@ -216,17 +216,26 @@

Guides

Created by the Bootstrap team · © 2023
- -; - + + From ab2a2698cf900c2f6792980599d166177aca6d3e Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Thu, 8 Aug 2024 23:18:57 +0500 Subject: [PATCH 17/71] update console.log --- src/widget/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/index.js b/src/widget/index.js index 61e22abc..e22298b0 100644 --- a/src/widget/index.js +++ b/src/widget/index.js @@ -268,7 +268,7 @@ const isSelectionLeftToRight = (selection) => { }; const handleTypoReporter = (options) => { - console.log('Script loaded:', new Date().getTime()); + console.log('commit 813a17e3e15d0d7adde1e1875a9034de5ee6e212'); if (!options || (!options.authorizationToken && !options.workSpaceId)) { throw new Error('Для работы модуля требуется указать workSpaceId и authorizationToken'); } From 6ced041d2df54c4da87bb294b926e7ffce7a310d Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Thu, 8 Aug 2024 23:26:24 +0500 Subject: [PATCH 18/71] fix load latest --- src/main/resources/templates/workspace/wks-integration.html | 6 +++--- src/widget/index.html | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/templates/workspace/wks-integration.html b/src/main/resources/templates/workspace/wks-integration.html index 0d070878..190249e8 100644 --- a/src/main/resources/templates/workspace/wks-integration.html +++ b/src/main/resources/templates/workspace/wks-integration.html @@ -21,11 +21,11 @@ }; document.addEventListener('DOMContentLoaded', function () { - const scriptSrc = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@main/src/widget/index.js?v=' + new Date().getTime(); + const scriptSrc = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@latest/src/widget/index.js?v=' + new Date().getTime(); loadScript(scriptSrc, function () { - handleTypoReporter({ + handleTypoReporter({ authorizationToken: '[[${wksBasicToken}]]', - workSpaceUrl: '[[${rootUrl}]]', + workSpaceUrl: '[[${rootUrl}]]', workSpaceId: '[[${wksId}]]'}) }); }); diff --git a/src/widget/index.html b/src/widget/index.html index 29ee1ed6..03c31ef3 100644 --- a/src/widget/index.html +++ b/src/widget/index.html @@ -227,7 +227,7 @@

Guides

}; document.addEventListener('DOMContentLoaded', function () { - const scriptSrc = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@main/src/widget/index.js?v=' + new Date().getTime(); + const scriptSrc = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@latest/src/widget/index.js?v=' + new Date().getTime(); loadScript(scriptSrc, function () { handleTypoReporter({ authorizationToken: 'MjQyOmVlYTZkMWE1LTNkMGQtNDg1Yi04OGMwLWVkOGU1YTRlOGZjMA==', From 03f03dba6ed035950bd6c71074fdef3f7e294f68 Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Fri, 9 Aug 2024 23:36:48 +0500 Subject: [PATCH 19/71] Update index.js --- src/widget/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/index.js b/src/widget/index.js index e22298b0..83c6db5d 100644 --- a/src/widget/index.js +++ b/src/widget/index.js @@ -268,7 +268,7 @@ const isSelectionLeftToRight = (selection) => { }; const handleTypoReporter = (options) => { - console.log('commit 813a17e3e15d0d7adde1e1875a9034de5ee6e212'); + console.log('commit 6ced041'); if (!options || (!options.authorizationToken && !options.workSpaceId)) { throw new Error('Для работы модуля требуется указать workSpaceId и authorizationToken'); } From 8b6a54a163238b7ed7cf553aaa9daed18672714d Mon Sep 17 00:00:00 2001 From: FC-LinuxMachine Date: Tue, 13 Aug 2024 21:48:55 +0700 Subject: [PATCH 20/71] fixes #291 Bug: User is registered with an incorrectly confirmed Password --- .../service/dto/FieldMatchConsiderCase.java | 21 ++--------- .../web/model/SignupAccountModel.java | 2 +- src/main/resources/messages_en.properties | 1 + src/main/resources/messages_ru.properties | 1 + .../typoreporter/web/SignupControllerIT.java | 37 +++++++++++++++++-- 5 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/hexlet/typoreporter/service/dto/FieldMatchConsiderCase.java b/src/main/java/io/hexlet/typoreporter/service/dto/FieldMatchConsiderCase.java index 3e3b489f..211f42d1 100644 --- a/src/main/java/io/hexlet/typoreporter/service/dto/FieldMatchConsiderCase.java +++ b/src/main/java/io/hexlet/typoreporter/service/dto/FieldMatchConsiderCase.java @@ -3,14 +3,11 @@ import jakarta.validation.Constraint; import jakarta.validation.Payload; -import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - /** * Validation annotation to validate that 2 fields have the same value. * An array of fields and their matching confirmation fields can be supplied. @@ -27,6 +24,8 @@ * message = "The password and it confirmation must match")} */ @Constraint(validatedBy = FieldMatchConsiderCaseValidator.class) +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) public @interface FieldMatchConsiderCase { String message() default "The {first} and {second} fields must be equal"; @@ -45,16 +44,4 @@ */ String second(); - /** - * Defines several @FieldMatch annotations on the same element - * - * @see FieldMatchConsiderCase - */ - @Target({TYPE, ANNOTATION_TYPE}) - @Retention(RUNTIME) - @Documented - @interface List { - - FieldMatchConsiderCase[] value(); - } } diff --git a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java index b475a10b..16b51637 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java @@ -19,7 +19,7 @@ @FieldMatchConsiderCase( first = "password", second = "confirmPassword", - message = "The password and it confirmation must match") + message = "{alert.passwords-dont-match}") @ToString public class SignupAccountModel { diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index 892b32df..8b1b2151 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -104,3 +104,4 @@ btn.delete-from-wks=Delete from workspace alert.password-wrong-format=Password must be between 8 and 20 characters \ and contain only latin letters, digits and symbols ~`!@#$%^&*()_-+={[}]|\:;"'<,>.?/ +alert.passwords-dont-match=Confirmation does not match the password diff --git a/src/main/resources/messages_ru.properties b/src/main/resources/messages_ru.properties index 65312c97..7da709f7 100644 --- a/src/main/resources/messages_ru.properties +++ b/src/main/resources/messages_ru.properties @@ -102,3 +102,4 @@ text.wks-delete-confirm=Удалить пространство? alert.password-wrong-format=Пароль должен быть от 8 до 20 символов\ \ и содержать только буквы латинского алфавита,\ \ цифры и символы ~`!@#$%^&*()_-+={[}]|\:;"'<,>.?/ +alert.passwords-dont-match=Подтверждение не совпадает с паролем diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index d0b5ce1b..5f9fdcc6 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -5,24 +5,26 @@ import io.hexlet.typoreporter.repository.AccountRepository; import io.hexlet.typoreporter.test.DBUnitEnumPostgres; import io.hexlet.typoreporter.web.model.SignupAccountModel; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.web.servlet.MockMvc; import org.springframework.transaction.annotation.Transactional; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.junit.jupiter.Container; import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; import static com.github.database.rider.core.api.configuration.Orthography.LOWERCASE; +import static io.hexlet.typoreporter.test.Constraints.POSTGRES_IMAGE; import static io.hexlet.typoreporter.test.factory.EntitiesFactory.ACCOUNT_INCORRECT_EMAIL; +import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.assertj.core.api.Assertions.assertThat; -import static io.hexlet.typoreporter.test.Constraints.POSTGRES_IMAGE; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @@ -66,6 +68,14 @@ static void datasourceProperties(DynamicPropertyRegistry registry) { "another_password", "another_password", "another_firstName", "another_lastName"); + private static ResourceBundleMessageSource source; + + @BeforeAll + static void init() { + source = new ResourceBundleMessageSource(); + source.setBasename("messages_en"); + } + @Test void createAccountWithIgnoreEmailCase() throws Exception { assertThat(accountRepository.count()).isEqualTo(0L); @@ -140,4 +150,23 @@ void signupInAccountWithBadEmail() throws Exception { var body = response.getResponse().getContentAsString(); assertThat(body).contains(String.format("The email "%s" is not valid", ACCOUNT_INCORRECT_EMAIL)); } + + @Test + void signupInPasswordsDontMatch() throws Exception { + model.setConfirmPassword("WrongPassword123"); + var response = mockMvc.perform(post("/signup") + .param("username", model.getUsername()) + .param("email", model.getEmail()) + .param("password", model.getPassword()) + .param("confirmPassword", model.getConfirmPassword()) + .param("firstName", model.getFirstName()) + .param("lastName", model.getLastName()) + .with(csrf())) + .andReturn(); + var body = response.getResponse().getContentAsString(); + + assertThat(body).contains(source.getMessage("alert.passwords-dont-match", null, null)); + + } + } From aca0992bde58427b280a69eb1b69ada7f15e5ff9 Mon Sep 17 00:00:00 2001 From: ymirotvorenie Date: Wed, 14 Aug 2024 16:47:31 +0700 Subject: [PATCH 21/71] Fix Issue#293. Add russian translate to email-placeholder at workspace->users --- src/main/resources/messages_en.properties | 1 + src/main/resources/messages_ru.properties | 1 + src/main/resources/templates/workspace/wks-users.html | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index 892b32df..d370eba2 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -66,6 +66,7 @@ wks.placeholder.descr=Workspace Description wks.info=Info wks.typos=Typos wks.users=Users +wks.users.email-placeholder=Enter user email. For example: hexlet@gmail.com wks.settings=Settings wks.urls=URLs wks.integration=Integration diff --git a/src/main/resources/messages_ru.properties b/src/main/resources/messages_ru.properties index 65312c97..351633c8 100644 --- a/src/main/resources/messages_ru.properties +++ b/src/main/resources/messages_ru.properties @@ -71,6 +71,7 @@ wks.placeholder.descr=Описание Пространства wks.info=Информация wks.typos=Опечатки wks.users=Пользователи +wks.users.email-placeholder=Введите адрес электронной почты пользователя. Например: hexlet@gmail.com wks.settings=Настройки wks.urls=Домены wks.integration=Интеграция diff --git a/src/main/resources/templates/workspace/wks-users.html b/src/main/resources/templates/workspace/wks-users.html index 642f6b0b..3e5f68f3 100644 --- a/src/main/resources/templates/workspace/wks-users.html +++ b/src/main/resources/templates/workspace/wks-users.html @@ -24,7 +24,7 @@
From 1b7fdf5e5b2f1b0ba42d82d8ab5df43a6b3fa75b Mon Sep 17 00:00:00 2001 From: FC-LinuxMachine Date: Thu, 15 Aug 2024 01:17:56 +0700 Subject: [PATCH 22/71] #254 fixes bug & add test for it --- .../controller/WorkspaceSettingsController.java | 9 ++++++++- .../web/WorkspaceSettingsControllerIT.java | 12 ++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java b/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java index 1802aadb..038d20b4 100644 --- a/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java +++ b/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java @@ -10,6 +10,7 @@ import io.hexlet.typoreporter.service.WorkspaceSettingsService; import io.hexlet.typoreporter.service.dto.typo.TypoInfo; import io.hexlet.typoreporter.service.dto.workspace.AllowedUrlDTO; +import io.hexlet.typoreporter.service.dto.workspace.WorkspaceInfo; import jakarta.servlet.http.HttpServletRequest; import jakarta.transaction.Transactional; import jakarta.validation.Valid; @@ -167,7 +168,9 @@ public String deleteAllowedUrl(Model model, @GetMapping("/integration") @PreAuthorize(IS_USER_RELATED_TO_WKS) public String getWorkspaceIntegrationPage(Model model, @PathVariable Long wksId, HttpServletRequest req) { - if (!workspaceService.existsWorkspaceById(wksId)) { + var wksOptional = workspaceService.getWorkspaceInfoById(wksId); + + if (wksOptional.isEmpty()) { //TODO send to error page log.error("Workspace with id {} not found", wksId); return "redirect:/workspaces"; @@ -175,6 +178,10 @@ public String getWorkspaceIntegrationPage(Model model, @PathVariable Long wksId, addTokenAndUrlToModel(model, wksId, req); + WorkspaceInfo wksInfo = wksOptional.get(); + model.addAttribute("wksInfo", wksInfo); + model.addAttribute("wksName", wksInfo.name()); + getStatisticDataToModel(model, wksId); getLastTypoDataToModel(model, wksId); diff --git a/src/test/java/io/hexlet/typoreporter/web/WorkspaceSettingsControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/WorkspaceSettingsControllerIT.java index 61de04be..5a84838c 100644 --- a/src/test/java/io/hexlet/typoreporter/web/WorkspaceSettingsControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/WorkspaceSettingsControllerIT.java @@ -83,18 +83,25 @@ static void datasourceProperties(DynamicPropertyRegistry registry) { @ParameterizedTest @MethodSource("io.hexlet.typoreporter.test.factory.EntitiesFactory#getWorkspacesAndUsersRelated") void getWorkspaceIntegrationPageIsSuccessful(final Long wksId, final String email) throws Exception { - final var apiAccessToken = workspaceSettingsRepository.getWorkspaceSettingsByWorkspaceId(wksId) + final var workspaces = workspaceSettingsRepository.getWorkspaceSettingsByWorkspaceId(wksId); + final var apiAccessToken = workspaces .map(s -> s.getId() + ":" + s.getApiAccessToken()) .map(String::getBytes) .map(Base64.getEncoder()::encodeToString) .orElse(null); + final var wksName = workspaces + .map(m -> m.getWorkspace().getName()) + .orElse(null); + MockHttpServletResponse response = mockMvc.perform(get("/workspace/{wksId}/integration", wksId.toString()) .with(user(email))) - .andExpect(model().attributeExists("wksBasicToken")) + .andExpect(model().attributeExists("wksBasicToken", "wksName")) .andReturn().getResponse(); assertThat(response.getContentAsString()).contains(apiAccessToken); + assertThat(response.getContentAsString()).contains(wksName); + } @ParameterizedTest @@ -213,4 +220,5 @@ void delAllowedUrlFromWorkspace() throws Exception { WORKSPACE_101_ID); assertThat(deletedAllowedUrlOptional).isEmpty(); } + } From 259a1b8960fe7724e6bb94f596b6d010ade9c122 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Thu, 15 Aug 2024 19:25:30 +0300 Subject: [PATCH 23/71] add test for signup account with empty names --- .../typoreporter/web/SignupControllerIT.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index d0b5ce1b..7fdd80ea 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -53,6 +53,7 @@ static void datasourceProperties(DynamicPropertyRegistry registry) { private static final String EMAIL_UPPER_CASE = "EMAIL_ADDRESS@GOOGLE.COM"; private static final String EMAIL_LOWER_CASE = EMAIL_UPPER_CASE.toLowerCase(); + private static final String EMPTY_NAME = ""; private final SignupAccountModel model = new SignupAccountModel( "model_upper_case", @@ -93,6 +94,22 @@ void createAccountWithIgnoreEmailCase() throws Exception { assertThat(accountRepository.count()).isEqualTo(1L); } + @Test + void createAccountWithEmptyNames() throws Exception { + model.setFirstName(EMPTY_NAME); + model.setLastName(EMPTY_NAME); + mockMvc.perform(post("/signup") + .param("username", model.getUsername()) + .param("email", model.getEmail()) + .param("password", model.getPassword()) + .param("confirmPassword", model.getConfirmPassword()) + .param("firstName", model.getFirstName()) + .param("lastName", model.getLastName()) + .with(csrf())) + .andReturn(); + assertThat(accountRepository.count()).isEqualTo(1L); + } + @Test void createAccountWithWrongEmailDomain() throws Exception { String userName = "testUser"; @@ -140,4 +157,5 @@ void signupInAccountWithBadEmail() throws Exception { var body = response.getResponse().getContentAsString(); assertThat(body).contains(String.format("The email "%s" is not valid", ACCOUNT_INCORRECT_EMAIL)); } + } From 0c12ef6c0fd51f4fb5dc581375fcf69b4bfd1885 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Thu, 15 Aug 2024 19:27:52 +0300 Subject: [PATCH 24/71] empty row --- src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index 7fdd80ea..15969006 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -157,5 +157,4 @@ void signupInAccountWithBadEmail() throws Exception { var body = response.getResponse().getContentAsString(); assertThat(body).contains(String.format("The email "%s" is not valid", ACCOUNT_INCORRECT_EMAIL)); } - } From 68b2c3c2732161688322ed660da18b143ab0d102 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Fri, 16 Aug 2024 14:21:14 +0300 Subject: [PATCH 25/71] fix createAccountWithEmptyNames test --- .../java/io/hexlet/typoreporter/web/SignupControllerIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index 15969006..ca70acc0 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -107,7 +107,7 @@ void createAccountWithEmptyNames() throws Exception { .param("lastName", model.getLastName()) .with(csrf())) .andReturn(); - assertThat(accountRepository.count()).isEqualTo(1L); + assertThat(accountRepository.findAccountByEmail(model.getEmail())).isNotEmpty(); } @Test From 5ed862f2fe2d2fd7cdd46e1d53a6f91a12ddcb3c Mon Sep 17 00:00:00 2001 From: uchitsa Date: Fri, 16 Aug 2024 14:22:17 +0300 Subject: [PATCH 26/71] fix lombok imports --- .../hexlet/typoreporter/web/model/SignupAccountModel.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java index 32f0a224..daa29773 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java @@ -5,7 +5,11 @@ import io.hexlet.typoreporter.service.dto.FieldMatchConsiderCase; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Size; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; @Getter @Setter From 4382def94daf4664416457fb8376e89aa0068933 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Fri, 16 Aug 2024 14:23:23 +0300 Subject: [PATCH 27/71] fix min size to 0 for names --- .../io/hexlet/typoreporter/web/model/SignupAccountModel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java index daa29773..f91f1736 100644 --- a/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java +++ b/src/main/java/io/hexlet/typoreporter/web/model/SignupAccountModel.java @@ -37,9 +37,9 @@ public class SignupAccountModel { @ToString.Exclude private String confirmPassword; - @Size(min = 1, max = 50) + @Size(max = 50) private String firstName; - @Size(min = 1, max = 50) + @Size(max = 50) private String lastName; } From 194b85ee6ccfc74504c1ec1698f63f687ceefd81 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Sat, 17 Aug 2024 02:29:11 +0300 Subject: [PATCH 28/71] fix min size to 0 for names --- .../java/io/hexlet/typoreporter/domain/account/Account.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/hexlet/typoreporter/domain/account/Account.java b/src/main/java/io/hexlet/typoreporter/domain/account/Account.java index 66b9ecc9..007757a0 100644 --- a/src/main/java/io/hexlet/typoreporter/domain/account/Account.java +++ b/src/main/java/io/hexlet/typoreporter/domain/account/Account.java @@ -73,12 +73,10 @@ public class Account extends AbstractAuditingEntity implements Identifiable Date: Sat, 17 Aug 2024 02:43:32 +0300 Subject: [PATCH 29/71] update createAccountWithEmptyNames test --- .../typoreporter/web/SignupControllerIT.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index ca70acc0..f41cec35 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -95,19 +95,20 @@ void createAccountWithIgnoreEmailCase() throws Exception { } @Test - void createAccountWithEmptyNames() throws Exception { - model.setFirstName(EMPTY_NAME); - model.setLastName(EMPTY_NAME); + void createAccountWithEmptyNames() throws Exception { + String email = "testEmptyNames@test.ru"; + String firstName = EMPTY_NAME; + String lastName = EMPTY_NAME; mockMvc.perform(post("/signup") .param("username", model.getUsername()) - .param("email", model.getEmail()) + .param("email", email) .param("password", model.getPassword()) .param("confirmPassword", model.getConfirmPassword()) - .param("firstName", model.getFirstName()) - .param("lastName", model.getLastName()) + .param("firstName", firstName) + .param("lastName", lastName) .with(csrf())) .andReturn(); - assertThat(accountRepository.findAccountByEmail(model.getEmail())).isNotEmpty(); + assertThat(accountRepository.findAccountByEmail(email)).isNotEmpty(); } @Test From 166ff718a327a5cdb1819b965631aa011996125e Mon Sep 17 00:00:00 2001 From: uchitsa Date: Sat, 17 Aug 2024 03:02:01 +0300 Subject: [PATCH 30/71] update createAccountWithEmptyNames test --- .../java/io/hexlet/typoreporter/web/SignupControllerIT.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index f41cec35..0880071b 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -95,12 +95,13 @@ void createAccountWithIgnoreEmailCase() throws Exception { } @Test - void createAccountWithEmptyNames() throws Exception { + void createAccountWithEmptyNames() throws Exception { + String userName = "testEmptyNamesUser"; String email = "testEmptyNames@test.ru"; String firstName = EMPTY_NAME; String lastName = EMPTY_NAME; mockMvc.perform(post("/signup") - .param("username", model.getUsername()) + .param("username", userName) .param("email", email) .param("password", model.getPassword()) .param("confirmPassword", model.getConfirmPassword()) From 8816f2ec5ea91b2b823d015a4a847d3759de3572 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Mon, 19 Aug 2024 01:25:07 +0300 Subject: [PATCH 31/71] Update SignupControllerIT.java --- .../typoreporter/web/SignupControllerIT.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index 0880071b..633576b6 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -96,20 +96,18 @@ void createAccountWithIgnoreEmailCase() throws Exception { @Test void createAccountWithEmptyNames() throws Exception { - String userName = "testEmptyNamesUser"; - String email = "testEmptyNames@test.ru"; - String firstName = EMPTY_NAME; - String lastName = EMPTY_NAME; + String emptyNamesUser = "testEmptyNamesUser"; + String emptyNamesEmail = "testEmptyNames@test.ru"; mockMvc.perform(post("/signup") - .param("username", userName) - .param("email", email) + .param("username", emptyNamesUser) + .param("email", emptyNamesEmail) .param("password", model.getPassword()) .param("confirmPassword", model.getConfirmPassword()) - .param("firstName", firstName) - .param("lastName", lastName) + .param("firstName", EMPTY_NAME) + .param("lastName", EMPTY_NAME) .with(csrf())) .andReturn(); - assertThat(accountRepository.findAccountByEmail(email)).isNotEmpty(); + assertThat(accountRepository.findAccountByEmail(emptyNamesEmail)).isNotEmpty(); } @Test From 185c9824e9f48112ff8ad7c6fb968bd2e4089811 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Mon, 19 Aug 2024 01:36:42 +0300 Subject: [PATCH 32/71] Update UpdateProfile.java --- .../typoreporter/service/dto/account/UpdateProfile.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java b/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java index 9f16a1fc..2715e2db 100644 --- a/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java +++ b/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java @@ -20,11 +20,9 @@ public class UpdateProfile { message = "The email \"${validatedValue}\" is not valid") private String email; - @NotBlank - @Size(min = 1, max = 50) + @Size(max = 50) private String firstName; - @NotBlank - @Size(min = 1, max = 50) + @Size(max = 50) private String lastName; } From 4099d624af378a89e3402ca7f7c7de98008826d2 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Mon, 19 Aug 2024 01:45:04 +0300 Subject: [PATCH 33/71] Update SignupControllerIT.java --- .../java/io/hexlet/typoreporter/web/SignupControllerIT.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index 633576b6..b55aed42 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -98,11 +98,12 @@ void createAccountWithIgnoreEmailCase() throws Exception { void createAccountWithEmptyNames() throws Exception { String emptyNamesUser = "testEmptyNamesUser"; String emptyNamesEmail = "testEmptyNames@test.ru"; + String emptyNamesPassword = "P@$$w0rd"; mockMvc.perform(post("/signup") .param("username", emptyNamesUser) .param("email", emptyNamesEmail) - .param("password", model.getPassword()) - .param("confirmPassword", model.getConfirmPassword()) + .param("password", emptyNamesPassword) + .param("confirmPassword", emptyNamesPassword) .param("firstName", EMPTY_NAME) .param("lastName", EMPTY_NAME) .with(csrf())) From 133ac700528fb48a302d15f5a3c63bc065f01325 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Mon, 19 Aug 2024 01:52:20 +0300 Subject: [PATCH 34/71] Update SignupControllerIT.java --- .../java/io/hexlet/typoreporter/web/SignupControllerIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index b55aed42..eaf0e396 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -97,7 +97,7 @@ void createAccountWithIgnoreEmailCase() throws Exception { @Test void createAccountWithEmptyNames() throws Exception { String emptyNamesUser = "testEmptyNamesUser"; - String emptyNamesEmail = "testEmptyNames@test.ru"; + String emptyNamesEmail = "testemptynames@test.ru"; String emptyNamesPassword = "P@$$w0rd"; mockMvc.perform(post("/signup") .param("username", emptyNamesUser) From 0185d3a77a8ccb67a3f2eba4720558ffd47b25ed Mon Sep 17 00:00:00 2001 From: uchitsa Date: Mon, 19 Aug 2024 01:59:59 +0300 Subject: [PATCH 35/71] Update UpdateProfile.java --- .../hexlet/typoreporter/service/dto/account/UpdateProfile.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java b/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java index 2715e2db..262fdb4b 100644 --- a/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java +++ b/src/main/java/io/hexlet/typoreporter/service/dto/account/UpdateProfile.java @@ -2,7 +2,6 @@ import io.hexlet.typoreporter.domain.account.constraint.AccountUsername; import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; From e76b3abe65b3573bb5ac60a2edde9eb4395eff1f Mon Sep 17 00:00:00 2001 From: FC-LinuxMachine Date: Wed, 21 Aug 2024 16:46:14 +0700 Subject: [PATCH 36/71] #254 updated test verification that the user hasn`t added to DB --- .../java/io/hexlet/typoreporter/web/SignupControllerIT.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index 5f9fdcc6..1393bfe5 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -164,8 +164,10 @@ void signupInPasswordsDontMatch() throws Exception { .with(csrf())) .andReturn(); var body = response.getResponse().getContentAsString(); + var result = accountRepository.findAccountByEmail(model.getEmail().toLowerCase()); assertThat(body).contains(source.getMessage("alert.passwords-dont-match", null, null)); + assertThat(result).isEmpty(); } From bb057c9674c4a76051d415a708431c735e2ab42c Mon Sep 17 00:00:00 2001 From: FC-LinuxMachine Date: Sun, 25 Aug 2024 19:20:02 +0700 Subject: [PATCH 37/71] #254 updated getWorkSpaceIntegrationPage for use Optional --- .../controller/WorkspaceSettingsController.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java b/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java index 038d20b4..6c4ec971 100644 --- a/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java +++ b/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java @@ -168,9 +168,9 @@ public String deleteAllowedUrl(Model model, @GetMapping("/integration") @PreAuthorize(IS_USER_RELATED_TO_WKS) public String getWorkspaceIntegrationPage(Model model, @PathVariable Long wksId, HttpServletRequest req) { - var wksOptional = workspaceService.getWorkspaceInfoById(wksId); + var wksOptional = workspaceService.getWorkspaceInfoById(wksId).orElse(null); - if (wksOptional.isEmpty()) { + if (wksOptional == null) { //TODO send to error page log.error("Workspace with id {} not found", wksId); return "redirect:/workspaces"; @@ -178,9 +178,8 @@ public String getWorkspaceIntegrationPage(Model model, @PathVariable Long wksId, addTokenAndUrlToModel(model, wksId, req); - WorkspaceInfo wksInfo = wksOptional.get(); - model.addAttribute("wksInfo", wksInfo); - model.addAttribute("wksName", wksInfo.name()); + model.addAttribute("wksInfo", wksOptional); + model.addAttribute("wksName", wksOptional.name()); getStatisticDataToModel(model, wksId); getLastTypoDataToModel(model, wksId); From 36300e2402b8edef66dc969548afd3950e3038f5 Mon Sep 17 00:00:00 2001 From: FC-LinuxMachine Date: Sun, 25 Aug 2024 20:21:18 +0700 Subject: [PATCH 38/71] #254 fix checkstyle --- .../typoreporter/controller/WorkspaceSettingsController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java b/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java index 6c4ec971..b7b95b0a 100644 --- a/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java +++ b/src/main/java/io/hexlet/typoreporter/controller/WorkspaceSettingsController.java @@ -10,7 +10,6 @@ import io.hexlet.typoreporter.service.WorkspaceSettingsService; import io.hexlet.typoreporter.service.dto.typo.TypoInfo; import io.hexlet.typoreporter.service.dto.workspace.AllowedUrlDTO; -import io.hexlet.typoreporter.service.dto.workspace.WorkspaceInfo; import jakarta.servlet.http.HttpServletRequest; import jakarta.transaction.Transactional; import jakarta.validation.Valid; From 3c0dc7d5d228467bec8a0578206da52c2b615f48 Mon Sep 17 00:00:00 2001 From: Grad566 <3008denis@gmail.com> Date: Mon, 26 Aug 2024 13:27:27 +0300 Subject: [PATCH 39/71] [#247] Rm element with the question from error report --- src/widget/index.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/widget/index.js b/src/widget/index.js index 83c6db5d..36b977ca 100644 --- a/src/widget/index.js +++ b/src/widget/index.js @@ -158,10 +158,6 @@ const generateModal = (state) => { const divSecondLabel = document.createElement('div'); divSecondLabel.classList.add('hexlet-correction-modal_ReportTypo-label'); - const strong = document.createElement('strong'); - strong.id = 'hexlet-correction-modal_question'; - strong.textContent = 'Отправить сообщение об ошибке редактору сайта?'; - divSecondLabel.append(strong); const divButtons = document.createElement('div'); divButtons.style.textAlign = 'right'; From a7e8a1fad0083da008bb802864dfc426254463f9 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Wed, 28 Aug 2024 00:53:47 +0300 Subject: [PATCH 40/71] #286 update SignupControllerIT.java --- .../typoreporter/web/SignupControllerIT.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index 03462f04..39f61c38 100644 --- a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java +++ b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java @@ -55,7 +55,6 @@ static void datasourceProperties(DynamicPropertyRegistry registry) { private static final String EMAIL_UPPER_CASE = "EMAIL_ADDRESS@GOOGLE.COM"; private static final String EMAIL_LOWER_CASE = EMAIL_UPPER_CASE.toLowerCase(); - private static final String EMPTY_NAME = ""; private final SignupAccountModel model = new SignupAccountModel( "model_upper_case", @@ -106,19 +105,20 @@ void createAccountWithIgnoreEmailCase() throws Exception { @Test void createAccountWithEmptyNames() throws Exception { - String emptyNamesUser = "testEmptyNamesUser"; - String emptyNamesEmail = "testemptynames@test.ru"; - String emptyNamesPassword = "P@$$w0rd"; + String username = "testEmptyNamesUser"; + String email = "testemptynames@test.ru"; + String password = "P@$$w0rd"; + String emptyName = ""; mockMvc.perform(post("/signup") - .param("username", emptyNamesUser) - .param("email", emptyNamesEmail) - .param("password", emptyNamesPassword) - .param("confirmPassword", emptyNamesPassword) - .param("firstName", EMPTY_NAME) - .param("lastName", EMPTY_NAME) + .param("username", username) + .param("email", email) + .param("password", password) + .param("confirmPassword", password) + .param("firstName", emptyName) + .param("lastName", emptyName) .with(csrf())) .andReturn(); - assertThat(accountRepository.findAccountByEmail(emptyNamesEmail)).isNotEmpty(); + assertThat(accountRepository.findAccountByEmail(email)).isNotEmpty(); } @Test From 49fdff22c1e3b7a632921751f6540bca3c16a9b6 Mon Sep 17 00:00:00 2001 From: uchitsa Date: Thu, 29 Aug 2024 02:01:41 +0300 Subject: [PATCH 41/71] #301 remove first name input --- src/main/resources/templates/account/signup.html | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/main/resources/templates/account/signup.html b/src/main/resources/templates/account/signup.html index 328c8fd2..d49b3440 100644 --- a/src/main/resources/templates/account/signup.html +++ b/src/main/resources/templates/account/signup.html @@ -29,17 +29,6 @@
-
- - -
-

-
-
-
Date: Thu, 29 Aug 2024 02:03:14 +0300 Subject: [PATCH 42/71] #301 remove last name input --- src/main/resources/templates/account/signup.html | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/main/resources/templates/account/signup.html b/src/main/resources/templates/account/signup.html index d49b3440..4af1f6e7 100644 --- a/src/main/resources/templates/account/signup.html +++ b/src/main/resources/templates/account/signup.html @@ -29,17 +29,6 @@
-
- - -
-

-
-
-
From ff6fd21c85fa45843af52cf5a404b4a6669a6982 Mon Sep 17 00:00:00 2001 From: shamshaev Date: Sun, 22 Sep 2024 18:28:15 +0300 Subject: [PATCH 43/71] Improve UX without workspace --- src/main/resources/messages_en.properties | 6 ++++++ src/main/resources/messages_ru.properties | 6 ++++++ src/main/resources/templates/workspaces.html | 17 +++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index d55eae14..1f808f83 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -51,6 +51,12 @@ navbar.signup=Sign Up navbar.create-workspace=Create Workspace navbar.account-info=Account Info +text.hero-header=Everything you need in +text.hero-highlight=one place +text.hero-description=Create your workspace to manage everything regarding typos: add sites (URLs) from which reports can be sent to the workspace, see and manage received typo statistics, add users to the workspace and more +btn.get-started=Get started +btn.learn-more=Learn more + acc-info.card-title.general=General acc-info.card-title.workspaces=Workspaces diff --git a/src/main/resources/messages_ru.properties b/src/main/resources/messages_ru.properties index 8d05ca07..07be78b1 100644 --- a/src/main/resources/messages_ru.properties +++ b/src/main/resources/messages_ru.properties @@ -57,6 +57,12 @@ navbar.signup=Регистрация navbar.account-info=Информация об аккаунте navbar.create-workspace=Создать Пространство +text.hero-header=Все что вам нужно в +text.hero-highlight=одном месте +text.hero-description=Создайте свое рабочее пространство, чтобы управлять всем связанным с опечатками: добавлять сайты (URLs) с которых отчеты могут отправляться в пространство, видеть и управлять статистикой полученных опечаток, добавлять пользователей в рабочее пространство и многое другое +btn.get-started=Начать +btn.learn-more=Узнать больше + acc-info.wks.url=URL пространства acc-info.wks.name=Имя пространства acc-info.wks.role=Роль diff --git a/src/main/resources/templates/workspaces.html b/src/main/resources/templates/workspaces.html index d3ef9aad..4db3c3b0 100644 --- a/src/main/resources/templates/workspaces.html +++ b/src/main/resources/templates/workspaces.html @@ -4,6 +4,23 @@
+
+
+
+

+ [[#{text.hero-header}]] + +

+
+
+

+
+
+ + +
+
+
From 7304bc34a5816431693933cf0c08238a3780823e Mon Sep 17 00:00:00 2001 From: shamshaev Date: Mon, 23 Sep 2024 21:50:59 +0300 Subject: [PATCH 44/71] Remove button Learn more --- src/main/resources/messages_en.properties | 1 - src/main/resources/messages_ru.properties | 1 - src/main/resources/templates/workspaces.html | 1 - 3 files changed, 3 deletions(-) diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties index 1f808f83..74e75d3f 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -55,7 +55,6 @@ text.hero-header=Everything you need in text.hero-highlight=one place text.hero-description=Create your workspace to manage everything regarding typos: add sites (URLs) from which reports can be sent to the workspace, see and manage received typo statistics, add users to the workspace and more btn.get-started=Get started -btn.learn-more=Learn more acc-info.card-title.general=General acc-info.card-title.workspaces=Workspaces diff --git a/src/main/resources/messages_ru.properties b/src/main/resources/messages_ru.properties index 07be78b1..53ddacf2 100644 --- a/src/main/resources/messages_ru.properties +++ b/src/main/resources/messages_ru.properties @@ -61,7 +61,6 @@ text.hero-header=Все что вам нужно в text.hero-highlight=одном месте text.hero-description=Создайте свое рабочее пространство, чтобы управлять всем связанным с опечатками: добавлять сайты (URLs) с которых отчеты могут отправляться в пространство, видеть и управлять статистикой полученных опечаток, добавлять пользователей в рабочее пространство и многое другое btn.get-started=Начать -btn.learn-more=Узнать больше acc-info.wks.url=URL пространства acc-info.wks.name=Имя пространства diff --git a/src/main/resources/templates/workspaces.html b/src/main/resources/templates/workspaces.html index 4db3c3b0..f8acd44b 100644 --- a/src/main/resources/templates/workspaces.html +++ b/src/main/resources/templates/workspaces.html @@ -17,7 +17,6 @@

-
From 389dd17dd0e9a1c57f9336157f87b7c92b2f9609 Mon Sep 17 00:00:00 2001 From: shamshaev Date: Tue, 24 Sep 2024 19:01:11 +0300 Subject: [PATCH 45/71] Fix issue #191 long url breaks layout --- src/main/resources/templates/workspace/wks-typos.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/workspace/wks-typos.html b/src/main/resources/templates/workspace/wks-typos.html index 3761826c..f73f20fd 100644 --- a/src/main/resources/templates/workspace/wks-typos.html +++ b/src/main/resources/templates/workspace/wks-typos.html @@ -148,7 +148,7 @@ th:aria-controls="|collapseTypoRowId_*{id}|" th:classappend="|table-*{typoStatus.getStyle}|" th:data-bs-target="|#collapseTypoRowId_*{id}|"> - + From a96d27f2374d202be69df40cb3df6e17c2d7ee15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Veronika=20Patakyov=C3=A1?= Date: Fri, 27 Sep 2024 16:32:28 +0200 Subject: [PATCH 46/71] Removed link from user email in navbar and added new classes to keep the styling --- src/main/resources/templates/fragments/panels.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/fragments/panels.html b/src/main/resources/templates/fragments/panels.html index 2c79c194..5866262f 100644 --- a/src/main/resources/templates/fragments/panels.html +++ b/src/main/resources/templates/fragments/panels.html @@ -31,7 +31,7 @@
+
diff --git a/src/main/resources/templates/account/pass-update.html b/src/main/resources/templates/account/pass-update.html index 00009fd4..bc08e7bf 100644 --- a/src/main/resources/templates/account/pass-update.html +++ b/src/main/resources/templates/account/pass-update.html @@ -1,41 +1,42 @@ - - -
-
-
- -
- - -
-
-
- - -
-
-
- - -
-
-
-
-
- - - + + +
+
+
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+
+
+ + +
+
-
-
+ +
diff --git a/src/main/resources/templates/account/prof-update.html b/src/main/resources/templates/account/prof-update.html index d9d3332e..28f631e2 100644 --- a/src/main/resources/templates/account/prof-update.html +++ b/src/main/resources/templates/account/prof-update.html @@ -1,45 +1,46 @@ - - -
-
-
-
-
- - -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - -
+ + +
+
+
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
-
-
+ +
diff --git a/src/main/resources/templates/account/signup.html b/src/main/resources/templates/account/signup.html index 4af1f6e7..124feed4 100644 --- a/src/main/resources/templates/account/signup.html +++ b/src/main/resources/templates/account/signup.html @@ -1,56 +1,56 @@ - - -
-
-
-
-
- - -
-

+ + +
+
+
+ +
+ + +
+

+
-
-
- - -
-

+
+ + +
+

+
-
-
- - -
-

+
+ + +
+

+
-
-
- - -
-

+
+ + +
+

+
-
- - + + +
-
-
- +
+
diff --git a/src/main/resources/templates/create-workspace.html b/src/main/resources/templates/create-workspace.html index 88c0a31f..8da3ff0b 100644 --- a/src/main/resources/templates/create-workspace.html +++ b/src/main/resources/templates/create-workspace.html @@ -1,39 +1,40 @@ - - -
-
-
-
-
- - -
-
-
- - -
-
-
- - -
-
- -
+ + +
+
+
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ +
+
-
-
+ +
diff --git a/src/main/resources/templates/error-general.html b/src/main/resources/templates/error-general.html index e8b7e77e..b98ba45a 100644 --- a/src/main/resources/templates/error-general.html +++ b/src/main/resources/templates/error-general.html @@ -6,4 +6,5 @@
+
diff --git a/src/main/resources/templates/fragments/footer.html b/src/main/resources/templates/fragments/footer.html index 20ea9a34..52a0a127 100644 --- a/src/main/resources/templates/fragments/footer.html +++ b/src/main/resources/templates/fragments/footer.html @@ -1,91 +1,67 @@ -