Skip to content

Commit 1a76540

Browse files
authored
fix: OTP entry order (#482)
* chore: otp entry order allow any digit order of entry * chore: code entry order support any code entry order * chore: test title add clearer test title and remove unneeded assertion * chore: string syntax remove unnecessary string templates
1 parent 5280004 commit 1a76540

File tree

3 files changed

+36
-6
lines changed

3 files changed

+36
-6
lines changed

docker/otp-provider/e2e/otp.spec.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { test, expect } from '@playwright/test';
22
import models from '../src/modules/sequelize/models';
33
import { config } from '../src/config';
4-
import { changeOTP, fillOTP, initURL, clientId } from './util';
4+
import {errors} from '../src/utils/shared'
5+
import { changeOTP, fillOTP, initURL, clientId, redirectURI } from './util';
56

67
const otpModel = models.get('Otp');
78
const eventModel = models.get('Event');
@@ -22,8 +23,7 @@ test('OTP Validations', async ({ page }, testInfo) => {
2223
await expect(page.getByRole('heading')).toMatchAriaSnapshot(`- heading "Enter your verification code" [level=2]`);
2324

2425
await page.getByRole('textbox', { name: 'Digit 1' }).fill('a');
25-
await expect(page.locator('#otp-error')).toMatchAriaSnapshot(`- text: OTP Must only include digits [0-9].`);
26-
26+
await expect(page.locator('#otp-error')).toHaveText(errors.OTP_TYPES);
2727
const currentOtp = await otpModel
2828
.findOne({ where: { email: `${testInfo.project.name}@b.com`, active: true } })
2929
.then((res) => res.otp);
@@ -36,6 +36,35 @@ test('OTP Validations', async ({ page }, testInfo) => {
3636
);
3737
});
3838

39+
test('OTP submits when all digits are filled regardless of order', async ({ page }, testInfo) => {
40+
await page.goto(initURL);
41+
42+
// Enter email and go to OTP page
43+
await page.getByRole('textbox', { name: 'Email' }).fill(`${testInfo.project.name}@b.com`);
44+
await page.getByRole('button', { name: 'Continue' }).click();
45+
await page.waitForURL('**/otp');
46+
await expect(page.getByRole('heading')).toMatchAriaSnapshot(`- heading "Enter your verification code" [level=2]`);
47+
48+
const currentOtp = await otpModel
49+
.findOne({ where: { email: `${testInfo.project.name}@b.com`, active: true } })
50+
.then((res) => res.otp);
51+
52+
// Fill the first 4 digits
53+
for (let i = 0; i < 4; i++) {
54+
await page.getByRole('textbox', { name: `Digit ${i + 1}` }).fill(currentOtp[i]);
55+
}
56+
// Fill the 6th digit
57+
await page.getByRole('textbox', { name: "Digit 6" }).fill(currentOtp[5]);
58+
59+
// Fill the 5th digit.
60+
await page.getByRole('textbox', { name: "Digit 5" }).fill(currentOtp[4]);
61+
62+
// Submission should run and send to the redirect
63+
await page.waitForRequest((req) => {
64+
return req.url().startsWith(redirectURI);
65+
});
66+
});
67+
3968
test('OTP Resend Code Countdown', async ({ page }, testInfo) => {
4069
await page.goto(initURL);
4170

docker/otp-provider/src/client/otp.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,14 @@ document.addEventListener('DOMContentLoaded', () => {
5757

5858
(e.target as HTMLInputElement).value = value;
5959
const nextInput = digitInputs[i + 1];
60-
if (nextInput) nextInput.focus();
61-
else {
60+
// Validate and submit whenever all inputs are filled
61+
if (digitInputs.every(input => input.value !== '')) {
6262
const codes = digitInputs.map((input) => input.value);
6363
const [, error] = otpValidator(codes);
6464
if (error) setFormError(errorEl, error as string);
6565
else form.requestSubmit();
6666
}
67+
else if (nextInput) nextInput.focus();
6768
});
6869

6970
input.addEventListener('paste', (e) => {

docker/otp-provider/src/utils/shared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const errors = {
1010
INVALID_EMAIL: 'Invalid email.',
1111
EXPIRED_OTP_WITH_RESEND: 'Your OTP has expired. Please request a new one to continue.',
1212
OTP_LENGTH: 'Please provide 6 digits.',
13-
OTP_TYPES: 'OTP Must only include digits [0-9].'
13+
OTP_TYPES: 'OTP must only include digits.'
1414
};
1515

1616
export const emailValidator = (email?: string): [boolean, null | string] => {

0 commit comments

Comments
 (0)