diff --git a/src/assets/locales/en.js b/src/assets/locales/en.js
index 539cad3ac..e297cf506 100644
--- a/src/assets/locales/en.js
+++ b/src/assets/locales/en.js
@@ -281,8 +281,13 @@ export default {
invalid: "OTP should be 6 digits long",
},
submit: "Submit OTP",
- resend: "Resend OTP",
- resent: "OTP has been sent again",
+ resend: {
+ no_timer: "Resend OTP",
+ timer: {
+ 1: "Resend OTP in ",
+ 2: " secs",
+ },
+ },
},
or: "OR",
google: {
diff --git a/src/assets/locales/hi.js b/src/assets/locales/hi.js
index 802262709..d323ac03c 100644
--- a/src/assets/locales/hi.js
+++ b/src/assets/locales/hi.js
@@ -283,8 +283,13 @@ export default {
invalid: "OTP 6 अंको का होना चाहिए",
},
submit: "OTP जमा करें",
- resend: "OTP दोबारा भेजो",
- resent: "OTP फिर भेजा गया है",
+ resend: {
+ no_timer: "OTP दोबारा भेजो",
+ timer: {
+ 1: "OTP वापस भेजें",
+ 2: "सेकंड में",
+ },
+ },
},
or: "या फिर",
google: {
diff --git a/src/components/UI/Text/InputNumber.vue b/src/components/UI/Text/InputNumber.vue
index 74e3fe43e..69e6e8d11 100644
--- a/src/components/UI/Text/InputNumber.vue
+++ b/src/components/UI/Text/InputNumber.vue
@@ -29,7 +29,7 @@
@@ -161,14 +161,10 @@
:titleConfig="resendOTPTitleConfig"
:buttonClass="resendOTPButtonClass"
class="mt-2"
- :isDisabled="submitOTPIconConfig.enabled"
- v-if="requestedOtp && !resentOtp"
+ :isDisabled="isSubmitOTPInProgress || !isResendOTPEnabled"
+ v-if="requestedOtp"
data-test="resendOTP"
>
-
-
- {{ $t("login.otp.resent") }}
-
@@ -216,7 +212,7 @@ export default {
isRequestOtpEnabled() {
if (!this.isRequestOtpEnabled) {
this.requestedOtp = false;
- this.resentOtp = false;
+ if (this.resendOTPTimer) clearInterval(this.otpTimerInterval);
}
},
},
@@ -224,8 +220,9 @@ export default {
return {
phoneInput: "", // phone input text
otpInput: "", // otp input text
+ resendOTPTimer: 0, // the count of the timer
+ otpTimerInterval: 0, // to reset the otp timer
requestedOtp: false, // whether the user has requested OTP once
- resentOtp: false, // whether the user has requested to resend OTP
invalidOtp: false, // whether the OTP is invalid
toast: useToast(),
warningIcon: require("@/assets/images/exclamation-circle-solid.svg"),
@@ -278,6 +275,13 @@ export default {
iconClass: "animate-spin h-4 object-scale-down text-white",
};
},
+ /**
+ * whether the button for resending OTP is enabled
+ */
+ isResendOTPEnabled() {
+ if (!this.resendOTPTimer) return true;
+ return false;
+ },
routeParams() {
// returns the params for where the user should be directed to
if (this.redirectTo == "" || this.redirectTo == "/") {
@@ -349,8 +353,16 @@ export default {
},
resendOTPTitleConfig() {
// title config for the resend OTP button
+ if (!this.isResendOTPEnabled) {
+ return {
+ value:
+ this.$t("login.otp.resend.timer.1") +
+ this.resendOTPTimer +
+ this.$t("login.otp.resend.timer.2"),
+ };
+ }
return {
- value: this.$t("login.otp.resend"),
+ value: this.$t("login.otp.resend.no_timer"),
};
},
resendOTPButtonClass() {
@@ -395,16 +407,31 @@ export default {
// whether the phone number entered by the user is valid
return this.phoneInput.toString().match(/^([0]|\+91)?[6-9]\d{9}$/g) != null;
},
+ /**
+ * counts seconds before enabling resend OTP button
+ *
+ * @param {Number} seconds - the number of seconds for which the timer is to be run
+ */
+ startResendOTPTimer(seconds = 60) {
+ this.resendOTPTimer = seconds;
+ this.otpTimerInterval = setInterval(() => {
+ this.resendOTPTimer--;
+ if (!this.resendOTPTimer) clearInterval(this.otpTimerInterval);
+ }, 1000);
+ },
requestOtp() {
// requests OTP for the first time
UserAPIService.requestOtp(this.formattedPhoneInput);
this.requestedOtp = true;
+ this.startResendOTPTimer();
+ this.otpInput = "";
this.invalidOtp = false;
},
resendOtp() {
// resends OTP on user request
UserAPIService.requestOtp(this.formattedPhoneInput);
- this.resentOtp = true;
+ this.startResendOTPTimer();
+ this.otpInput = "";
this.invalidOtp = false;
},
phoneLogin() {
diff --git a/tests/unit/pages/Login.spec.js b/tests/unit/pages/Login.spec.js
index 16ae19b88..6f39595fc 100644
--- a/tests/unit/pages/Login.spec.js
+++ b/tests/unit/pages/Login.spec.js
@@ -56,8 +56,8 @@ describe("Login.vue", () => {
mountWrapper();
await setPhoneNumber();
await requestOTP();
-
expect(requestOtp).toHaveBeenCalled();
+ expect(wrapper.vm.otpInput).toBe("");
expect(wrapper.vm.invalidOtp).toBe(false);
expect(wrapper.vm.requestedOtp).toBe(true);
expect(wrapper.find('[data-test="otp"]').exists()).toBe(true);
@@ -88,7 +88,6 @@ describe("Login.vue", () => {
.setValue("919191919");
expect(wrapper.vm.requestedOtp).toBe(false);
- expect(wrapper.vm.resentOtp).toBe(false);
});
it("resends OTP upon requesting", async () => {
@@ -96,21 +95,45 @@ describe("Login.vue", () => {
const requestOtp = jest
.spyOn(UserAPIService, "requestOtp")
.mockImplementation(() => jest.fn());
-
+ const resendOtp = jest.spyOn(Login.methods, "resendOtp");
+ const startResendOTPTimer = jest.spyOn(
+ Login.methods,
+ "startResendOTPTimer"
+ );
mountWrapper();
+
await setPhoneNumber();
+ jest.useFakeTimers();
await requestOTP();
+ await jest.advanceTimersByTime(60000);
+ // reset timers
+ jest.useRealTimers();
// resend OTP
await wrapper.find('[data-test="resendOTP"]').trigger("click");
-
await flushPromises();
expect(requestOtp).toHaveBeenCalled();
- expect(wrapper.vm.resentOtp).toBe(true);
+ expect(resendOtp).toHaveBeenCalled();
+ expect(startResendOTPTimer).toHaveBeenCalled();
+ expect(wrapper.vm.otpInput).toBe("");
+ expect(wrapper.vm.resendOTPTimer).toBe(60);
+ expect(wrapper.vm.isResendOTPEnabled).toBe(false);
expect(wrapper.vm.invalidOtp).toBe(false);
});
+ it("enables ResendOTP button when timer ends", async () => {
+ // fake timer is needed for this test case to advance the time of interval
+ jest.useFakeTimers();
+ const numseconds = 2;
+ wrapper.vm.startResendOTPTimer(numseconds);
+ jest.advanceTimersByTime(numseconds * 1000);
+ expect(wrapper.vm.isResendOTPEnabled).toBe(true);
+ expect(wrapper.vm.resendOTPTimer).toBe(0);
+ // reset timers
+ jest.useRealTimers();
+ });
+
describe("submit valid otp", () => {
let verifyOtp;
let getUserByAccessToken;