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 0585010a..055660df 100644 --- a/src/main/resources/messages_en.properties +++ b/src/main/resources/messages_en.properties @@ -68,6 +68,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 @@ -107,3 +108,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 ~`!@#$%^&*()_-+={[}]|\:;"'<,>.?/ link.sign-in-with-github=Sign in with GitHub +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 6bb51d97..c102a8bd 100644 --- a/src/main/resources/messages_ru.properties +++ b/src/main/resources/messages_ru.properties @@ -73,6 +73,7 @@ wks.placeholder.descr=Описание Пространства wks.info=Информация wks.typos=Опечатки wks.users=Пользователи +wks.users.email-placeholder=Введите адрес электронной почты пользователя. Например: hexlet@gmail.com wks.settings=Настройки wks.urls=Домены wks.integration=Интеграция @@ -105,3 +106,4 @@ alert.password-wrong-format=Пароль должен быть от 8 до 20 с \ и содержать только буквы латинского алфавита,\ \ цифры и символы ~`!@#$%^&*()_-+={[}]|\:;"'<,>.?/ link.sign-in-with-github=Войти с помощью: GitHub +alert.passwords-dont-match=Подтверждение не совпадает с паролем diff --git a/src/main/resources/templates/workspace/wks-integration.html b/src/main/resources/templates/workspace/wks-integration.html index f0d431b2..190249e8 100644 --- a/src/main/resources/templates/workspace/wks-integration.html +++ b/src/main/resources/templates/workspace/wks-integration.html @@ -6,19 +6,33 @@
-
+

-<script src="https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@main/src/widget/index.js"></script>;
-
 <script>
-    handleTypoReporter({ authorizationToken: '[[${wksBasicToken}]]',
-    workSpaceUrl: '[[${rootUrl}]]', workSpaceId: '[[${wksId}]]'})
-</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 scriptSrc = 'https://cdn.jsdelivr.net/gh/hexlet/hexlet-correction@latest/src/widget/index.js?v=' + new Date().getTime(); + loadScript(scriptSrc, function () { + handleTypoReporter({ + authorizationToken: '[[${wksBasicToken}]]', + workSpaceUrl: '[[${rootUrl}]]', + workSpaceId: '[[${wksId}]]'}) + }); +}); +</script> + +
+
diff --git a/src/main/resources/templates/workspace/wks-users.html b/src/main/resources/templates/workspace/wks-users.html index b7023c12..3e5f68f3 100644 --- a/src/main/resources/templates/workspace/wks-users.html +++ b/src/main/resources/templates/workspace/wks-users.html @@ -24,7 +24,7 @@
@@ -103,7 +103,7 @@ - + @@ -121,24 +121,8 @@ - - -
-
-
- -
- - - -
-
-
-
- - + +
diff --git a/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java b/src/test/java/io/hexlet/typoreporter/web/SignupControllerIT.java index d0b5ce1b..1393bfe5 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,25 @@ 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(); + var result = accountRepository.findAccountByEmail(model.getEmail().toLowerCase()); + + assertThat(body).contains(source.getMessage("alert.passwords-dont-match", null, null)); + assertThat(result).isEmpty(); + + } + } diff --git a/src/widget/index.html b/src/widget/index.html index c2757d69..03c31ef3 100644 --- a/src/widget/index.html +++ b/src/widget/index.html @@ -216,11 +216,26 @@

Guides

Created by the Bootstrap team · © 2023
- - + diff --git a/src/widget/index.js b/src/widget/index.js index 451858e2..83c6db5d 100644 --- a/src/widget/index.js +++ b/src/widget/index.js @@ -238,13 +238,13 @@ const sendData = (elements, state) => async (event) => { 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; - }, - }); + 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) { @@ -258,17 +258,18 @@ 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) { + console.log('commit 6ced041'); + if (!options || (!options.authorizationToken && !options.workSpaceId)) { throw new Error('Для работы модуля требуется указать workSpaceId и authorizationToken'); } @@ -307,13 +308,13 @@ 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); } 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);