366 lines
9.1 KiB
Markdown
366 lines
9.1 KiB
Markdown
|
|
# QA report: ERV Hotels claim form
|
|||
|
|
|
|||
|
|
Date: 2026-03-13
|
|||
|
|
URL: https://erv.clientright.ru/hotels/
|
|||
|
|
Scope: manual code-assisted QA review of masks, validation, policy check flow, SMS flow, step logic, and submission flow.
|
|||
|
|
|
|||
|
|
## Executive summary
|
|||
|
|
|
|||
|
|
The form is generally wired up and backend endpoints respond, but there are several important frontend issues.
|
|||
|
|
|
|||
|
|
Most important findings:
|
|||
|
|
1. **Critical:** form submission appears to bypass real SMS-confirmation gating.
|
|||
|
|
2. **High:** `Tab` is blocked on the first input field.
|
|||
|
|
3. **Medium:** jQuery is loaded twice.
|
|||
|
|
4. **Medium:** policy-number normalization is incomplete and fragile.
|
|||
|
|
5. **Medium:** phone validation logic is strict and should be regression-tested with paste scenarios.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Confirmed observations
|
|||
|
|
|
|||
|
|
### Backend policy check responds correctly
|
|||
|
|
Endpoint: `database.php`
|
|||
|
|
|
|||
|
|
Tested cases:
|
|||
|
|
- empty payload → returns `{"success":"false","message":"Номер полиса не указан"}`
|
|||
|
|
- invalid policy number → returns `{"success":"false","message":"Полис не найден"}`
|
|||
|
|
|
|||
|
|
This part looks alive and generally sane.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Findings
|
|||
|
|
|
|||
|
|
## 1) CRITICAL — submit flow appears to bypass actual SMS confirmation
|
|||
|
|
|
|||
|
|
### Why this is critical
|
|||
|
|
In the submit handler, the code opens the SMS modal but still proceeds directly to `submit.php` because the condition is hardcoded as `if(1)`.
|
|||
|
|
|
|||
|
|
### Relevant code pattern
|
|||
|
|
```js
|
|||
|
|
$('.form.active form').submit(function(e){
|
|||
|
|
if(!validate_step(index)){ e.preventDefault(); } else {
|
|||
|
|
e.preventDefault();
|
|||
|
|
$('button[type="submit"]').attr("disabled", true);
|
|||
|
|
|
|||
|
|
if(1) {
|
|||
|
|
$.fancybox.open({ src: '#confirm_sms', type: 'inline' });
|
|||
|
|
...
|
|||
|
|
$.ajax({
|
|||
|
|
url: 'submit.php',
|
|||
|
|
method: 'post',
|
|||
|
|
...
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Risk
|
|||
|
|
The form may be submitted even if SMS code was not actually confirmed.
|
|||
|
|
|
|||
|
|
### Expected behavior
|
|||
|
|
Submission to `submit.php` should only happen after successful SMS verification.
|
|||
|
|
|
|||
|
|
### Recommended fix
|
|||
|
|
Replace the hardcoded `if(1)` with a real condition based on verified phone state, for example:
|
|||
|
|
- a dedicated boolean like `smsVerified === true`
|
|||
|
|
- or a validated marker on the phone field after successful verification
|
|||
|
|
- and prevent submit entirely until SMS verification succeeds
|
|||
|
|
|
|||
|
|
### Suggested implementation direction
|
|||
|
|
- set explicit state after successful `sms-verify.php?action=verify`
|
|||
|
|
- on form submit, check that state
|
|||
|
|
- only then build `FormData` and call `submit.php`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2) HIGH — Tab key is blocked on the first input
|
|||
|
|
|
|||
|
|
### Relevant code
|
|||
|
|
```js
|
|||
|
|
document.querySelector('input').addEventListener('keydown', function (e) {
|
|||
|
|
if (e.which == 9) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Problem
|
|||
|
|
This blocks `Tab` on the first input field.
|
|||
|
|
|
|||
|
|
### User impact
|
|||
|
|
- keyboard navigation is broken
|
|||
|
|
- accessibility is degraded
|
|||
|
|
- desktop UX becomes weird and frustrating
|
|||
|
|
|
|||
|
|
### Expected behavior
|
|||
|
|
`Tab` should move focus to the next field normally.
|
|||
|
|
|
|||
|
|
### Recommended fix
|
|||
|
|
Remove this listener entirely unless there is a very specific documented reason.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3) MEDIUM — jQuery is included twice
|
|||
|
|
|
|||
|
|
### Current page includes
|
|||
|
|
```html
|
|||
|
|
<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
|
|||
|
|
<script src="libs/jquery/jquery-3.4.1.min.js"></script>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Risk
|
|||
|
|
- plugin conflicts
|
|||
|
|
- unexpected behavior depending on initialization order
|
|||
|
|
- harder debugging
|
|||
|
|
|
|||
|
|
### Expected behavior
|
|||
|
|
Only one jQuery version should be loaded.
|
|||
|
|
|
|||
|
|
### Recommended fix
|
|||
|
|
Keep a single version and verify plugins against it.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4) MEDIUM — policy number normalization is incomplete / fragile
|
|||
|
|
|
|||
|
|
### Current behavior
|
|||
|
|
The code only explicitly normalizes Cyrillic:
|
|||
|
|
- `Е` → `E`
|
|||
|
|
- `А` → `A`
|
|||
|
|
|
|||
|
|
### Relevant logic
|
|||
|
|
```js
|
|||
|
|
if(police_number.slice(0,1)=="Е"){
|
|||
|
|
police_number=police_number.replace(/Е/g, 'E');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if(police_number.slice(0,1)=="А"){
|
|||
|
|
police_number=police_number.replace(/А/g, 'A');
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Risk
|
|||
|
|
User input may still fail in edge cases:
|
|||
|
|
- pasted values with spaces
|
|||
|
|
- other Cyrillic lookalikes
|
|||
|
|
- lowercase input
|
|||
|
|
- mixed formatting
|
|||
|
|
|
|||
|
|
### Expected behavior
|
|||
|
|
Policy number should be normalized consistently before validation / sending.
|
|||
|
|
|
|||
|
|
### Recommended fix
|
|||
|
|
Before submit/check:
|
|||
|
|
- `trim()`
|
|||
|
|
- uppercase
|
|||
|
|
- replace common Cyrillic lookalikes with Latin equivalents
|
|||
|
|
- normalize spaces
|
|||
|
|
- possibly normalize dash types
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5) MEDIUM — applicant phone validation needs regression testing for paste/input scenarios
|
|||
|
|
|
|||
|
|
### Current logic
|
|||
|
|
Applicant phone (`.js-phone-mask`) is normalized to Russian 10-digit local format and validated strictly.
|
|||
|
|
|
|||
|
|
### Relevant behavior
|
|||
|
|
- strips `+7` / `8`
|
|||
|
|
- keeps only 10 digits
|
|||
|
|
- formats as `999 999-99-99`
|
|||
|
|
|
|||
|
|
### Potential issue areas
|
|||
|
|
- paste of `+7 912 345-67-89`
|
|||
|
|
- paste of `8 (912) 345-67-89`
|
|||
|
|
- typing starting with `8` or `7`
|
|||
|
|
- editing in the middle of the string
|
|||
|
|
- overlong paste values
|
|||
|
|
|
|||
|
|
### Expected behavior
|
|||
|
|
All common Russian user inputs should normalize cleanly and predictably.
|
|||
|
|
|
|||
|
|
### Recommended regression tests
|
|||
|
|
- `9123456789`
|
|||
|
|
- `8 912 345-67-89`
|
|||
|
|
- `+7 912 345-67-89`
|
|||
|
|
- `89123456789`
|
|||
|
|
- `79123456789`
|
|||
|
|
- paste with brackets/spaces/dashes
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6) MEDIUM — hotel phone field uses separate validation logic and should be tested independently
|
|||
|
|
|
|||
|
|
### Current logic
|
|||
|
|
Hotel phone (`.js-place-phone`) allows 10 to 15 digits.
|
|||
|
|
|
|||
|
|
### Risk
|
|||
|
|
International phone scenarios may behave inconsistently:
|
|||
|
|
- with `+`
|
|||
|
|
- with spaces
|
|||
|
|
- with brackets
|
|||
|
|
- with short local numbers
|
|||
|
|
|
|||
|
|
### Expected behavior
|
|||
|
|
Visible, predictable validation for international hotel contact numbers.
|
|||
|
|
|
|||
|
|
### Recommended regression tests
|
|||
|
|
- `+49 30 12345678`
|
|||
|
|
- `+1 (555) 123-4567`
|
|||
|
|
- `0049...`
|
|||
|
|
- short invalid numbers
|
|||
|
|
- numbers over 15 digits
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7) MEDIUM — description field has hard limits; UX should be checked
|
|||
|
|
|
|||
|
|
### Current logic
|
|||
|
|
Description requires:
|
|||
|
|
- minimum 50 characters
|
|||
|
|
- maximum 2000 characters
|
|||
|
|
|
|||
|
|
### Risk
|
|||
|
|
Users may be blocked without clear enough guidance.
|
|||
|
|
|
|||
|
|
### Expected behavior
|
|||
|
|
- clear live or submit-time feedback
|
|||
|
|
- understandable character requirement
|
|||
|
|
|
|||
|
|
### Recommended improvement
|
|||
|
|
Consider a live character counter or helper text.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8) MEDIUM — file requirements for selected hotel risks should be regression-tested in browser
|
|||
|
|
|
|||
|
|
### Current logic
|
|||
|
|
On `hotel-2`:
|
|||
|
|
- at least one risk checkbox must be selected
|
|||
|
|
- if a risk is selected, a supporting file is required
|
|||
|
|
- unchecking should clear file state
|
|||
|
|
|
|||
|
|
### Why this matters
|
|||
|
|
This logic is easy to break in real browser interaction.
|
|||
|
|
|
|||
|
|
### Recommended tests
|
|||
|
|
- select risk without file → should block next/submit
|
|||
|
|
- add file → should pass
|
|||
|
|
- uncheck risk after upload → file state should reset cleanly
|
|||
|
|
- re-check and re-upload → should still work
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9) LOW — duplicated / potentially confusing SMS UI flow
|
|||
|
|
|
|||
|
|
### Observed behavior in code
|
|||
|
|
The SMS modal and button state handling are fairly complex:
|
|||
|
|
- cooldown timer
|
|||
|
|
- resend behavior
|
|||
|
|
- modal warnings
|
|||
|
|
- direct phone field locking after success
|
|||
|
|
|
|||
|
|
This may be fine, but it is sensitive to regressions.
|
|||
|
|
|
|||
|
|
### Recommendation
|
|||
|
|
Simplify state handling where possible and ensure there is a single source of truth:
|
|||
|
|
- not verified
|
|||
|
|
- sending
|
|||
|
|
- code sent
|
|||
|
|
- verified
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Suggested fixes by priority
|
|||
|
|
|
|||
|
|
## Priority P1
|
|||
|
|
1. Fix submit flow so `submit.php` is called only after successful SMS verification.
|
|||
|
|
2. Remove the `Tab` blocker on the first input.
|
|||
|
|
|
|||
|
|
## Priority P2
|
|||
|
|
3. Remove duplicate jQuery include.
|
|||
|
|
4. Harden policy number normalization.
|
|||
|
|
|
|||
|
|
## Priority P3
|
|||
|
|
5. Regression-test applicant phone paste/typing behavior.
|
|||
|
|
6. Regression-test hotel phone international input behavior.
|
|||
|
|
7. Regression-test file upload behavior on `hotel-2`.
|
|||
|
|
8. Improve UX around description-length validation.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Suggested manual QA checklist for Cursor / further automation
|
|||
|
|
|
|||
|
|
### Policy check
|
|||
|
|
- empty policy number
|
|||
|
|
- invalid policy number
|
|||
|
|
- valid-format policy number with slash
|
|||
|
|
- Cyrillic first letter (`А`, `Е`)
|
|||
|
|
- paste with spaces
|
|||
|
|
|
|||
|
|
### Date of birth / representative block
|
|||
|
|
- adult DOB
|
|||
|
|
- minor DOB
|
|||
|
|
- representative block shown only for minors
|
|||
|
|
- representative fields disabled/hidden correctly otherwise
|
|||
|
|
|
|||
|
|
### Applicant phone
|
|||
|
|
- direct typing: `9123456789`
|
|||
|
|
- paste: `+7 912 345-67-89`
|
|||
|
|
- paste: `8 (912) 345-67-89`
|
|||
|
|
- reject incomplete values
|
|||
|
|
- reject non-digit garbage cleanly
|
|||
|
|
|
|||
|
|
### Hotel phone
|
|||
|
|
- accept international-looking values
|
|||
|
|
- reject too short values
|
|||
|
|
- reject too long values
|
|||
|
|
|
|||
|
|
### Email
|
|||
|
|
- valid email
|
|||
|
|
- invalid email without domain
|
|||
|
|
- invalid email without `@`
|
|||
|
|
- trim spaces around value
|
|||
|
|
|
|||
|
|
### Files
|
|||
|
|
- required file missing for selected risk
|
|||
|
|
- file upload passes when attached
|
|||
|
|
- file reset after uncheck
|
|||
|
|
- re-upload after returning back in steps
|
|||
|
|
|
|||
|
|
### Description
|
|||
|
|
- under 50 chars → error
|
|||
|
|
- exactly 50 chars → pass
|
|||
|
|
- over 2000 chars → error
|
|||
|
|
|
|||
|
|
### Final submit
|
|||
|
|
- no agreement checkbox → block
|
|||
|
|
- with agreement → allow
|
|||
|
|
- ensure SMS verified state is mandatory before actual submit
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Important note
|
|||
|
|
This report is based on:
|
|||
|
|
- live endpoint checks
|
|||
|
|
- frontend source review
|
|||
|
|
- flow analysis of production JS
|
|||
|
|
|
|||
|
|
A full browser-driven E2E pass is still recommended after fixes, especially for:
|
|||
|
|
- file uploads
|
|||
|
|
- modal transitions
|
|||
|
|
- SMS flow
|
|||
|
|
- step navigation
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Best next step for Cursor
|
|||
|
|
|
|||
|
|
Ask Cursor to:
|
|||
|
|
1. inspect `common.js`
|
|||
|
|
2. fix the submit/SMS gating bug first
|
|||
|
|
3. remove the `Tab` blocker
|
|||
|
|
4. remove duplicate jQuery include
|
|||
|
|
5. add robust input normalization for policy/phone
|
|||
|
|
6. produce a small browser regression checklist after patching
|