32 login page test cases, including the ones most sets leave out
Thirty-two test cases for a login page — the happy paths plus the negative, session, security and accessibility checks most published sets skip.
- 32 cases
- 8 coverage types
- Last verified Sep 16, 2026
A login page looks like the simplest screen in the product. Two fields and a button. That is exactly why it is usually the least tested — most suites check that the right password works and the wrong one does not, and stop there.
The interesting failures are elsewhere. The sign-out that leaves the session alive on the server. The error message that quietly confirms which email addresses have accounts. The form that cannot be completed without a mouse. None of those show up in a happy-path run, and all of them are things a real user or a real attacker will find.
Below are thirty-two cases covering that whole surface. Filter them by type, or take the CSV into your own tool and delete the ones that do not apply.
Showing 32 of 32 cases
| ID | Test case | Type | Priority | Preconditions | Steps | Test data | Expected result |
|---|---|---|---|---|---|---|---|
| LOGIN-01 | Sign in with a valid email and password | Functional | High | A verified account exists |
| [email protected] / Correct-Horse-9 | Signed in and sent to the default landing page. |
| LOGIN-02 | Land on the page you originally asked for | Functional | High | A verified account exists |
| /settings/billing | Sent to the deep link after sign-in, not to the dashboard. |
| LOGIN-03 | Email is matched without regard to case | Functional | Medium | An account registered as [email protected] |
| [email protected] | Signed in. Email case is not a reason to reject. |
| LOGIN-04 | Remember me survives closing the browser | Functional | Medium | A verified account exists |
| — | Still signed in, with a persistent cookie whose expiry matches the stated period. |
| LOGIN-05 | The show password control reveals what was typed | Functional | Medium | — |
| Correct-Horse-9 | The typed characters are shown, and the control reports its pressed state. |
| LOGIN-06 | Enter submits the form from either field | Functional | Medium | — |
| — | The form submits both times. No need to reach for the button. |
| LOGIN-07 | Sign out returns you to the signed-out state | Functional | High | Signed in |
| — | Sent to the signed-out page, and the protected pages no longer open. |
| LOGIN-08 | Sign-in attempt is refused when a valid email has the wrong password | Negative | High | A verified account exists |
| [email protected] / wrong-password | Refused with a message that does not say which of the two was wrong. |
| LOGIN-09 | An unknown email gives the same answer, in the same time | Negative | High | No account for the address used |
| [email protected] / anything | Identical message. Response times are close enough that the difference cannot be used to enumerate accounts. |
| LOGIN-10 | An empty form is stopped before it reaches the server | Negative | High | — |
| — | Both fields are marked as required and no network request is made. |
| LOGIN-11 | A password of only spaces is refused | Negative | Medium | — |
| [email protected] / " " | Refused. Whitespace alone is not a password. |
| LOGIN-12 | An unverified account cannot sign in | Negative | Medium | An account exists whose email is not yet confirmed |
| [email protected] | Refused with a message that explains verification is pending and offers to resend it. |
| LOGIN-13 | A disabled account is refused | Negative | Medium | An account has been disabled by an admin |
| [email protected] | Refused. The message does not read as a wrong-password error. |
| LOGIN-14 | Quotes and SQL fragments are treated as ordinary text | Negative | Medium | — |
| ' OR 1=1 -- | Refused as an invalid address. No database error is shown and nothing is signed in. |
| LOGIN-15 | A 64-character password is accepted | Boundary | Medium | An account whose password is 64 characters |
| 64 random characters | Accepted. Long passwords are not silently truncated. |
| LOGIN-16 | A 254-character email address is accepted | Boundary | Medium | An account at the longest legal address |
| 254-character address | Accepted. The field does not cut the address short. |
| LOGIN-17 | A password one character under the minimum is refused | Boundary | Medium | Minimum length is 8 |
| 7 characters | Refused, with the minimum stated in the message. |
| LOGIN-18 | Repeated failures are throttled | Security | High | A verified account exists |
| wrong-password | Attempts slow down or are blocked after the documented threshold, and the block is reported to the user. |
| LOGIN-19 | The session cookie carries the right flags | Security | High | Signed in over HTTPS |
| — | HttpOnly and Secure are set, SameSite is Lax or Strict, and there is no readable copy in local storage. |
| LOGIN-20 | The session identifier changes after signing in | Security | High | A session id exists before sign-in |
| — | The identifier is different. The pre-login one no longer works. |
| LOGIN-21 | Credentials never appear in a URL, a log or a referrer | Security | High | Proxy or network log available |
| Correct-Horse-9 | The password appears only in the encrypted request body. Nothing in a query string, an access log or a Referer header. |
| LOGIN-22 | Plain HTTP is redirected before the form is shown | Security | High | — |
| http://app.example.com/login | Redirected to https before any field is rendered, with HSTS on the response. |
| LOGIN-23 | The sign-in request carries a CSRF token | Security | Medium | — |
| — | The replay is rejected. A login form that accepts a cross-origin POST is a login CSRF. |
| LOGIN-24 | Signing out invalidates the session on the server | Session | High | Signed in, with a copy of the session cookie |
| — | The replay is rejected. Clearing the cookie in the browser is not enough on its own. |
| LOGIN-25 | An idle session expires | Session | Medium | Signed in; idle timeout is documented |
| — | The action is refused and the user is sent back to sign in, with no partial write. |
| LOGIN-26 | Changing the password ends the other sessions | Session | Medium | Signed in on two browsers |
| — | Browser B is signed out. A stolen session does not outlive the password it came from. |
| LOGIN-27 | Every field has a label a screen reader can read | Accessibility | High | — |
| — | Each control has a programmatic name. A placeholder alone is not a label. |
| LOGIN-28 | The error is announced, not just shown | Accessibility | High | Screen reader running |
| wrong-password | The message is announced through a live region and is linked to the field with aria-describedby. |
| LOGIN-29 | The whole form works with the keyboard alone | Accessibility | High | — |
| — | Focus order is email, password, reveal, remember, submit, forgot password. Focus is visible at every stop and never trapped. |
| LOGIN-30 | Text stays usable at 400% zoom | Accessibility | Medium | — |
| — | The form reflows to one column with no horizontal scrolling and nothing clipped. |
| LOGIN-31 | A password manager fills and submits the form | Compatibility | Medium | A saved credential in the browser password manager |
| — | Both fields fill and the sign-in succeeds. The autocomplete attributes are username and current-password. |
| LOGIN-32 | Google sign-in links to the existing account | Integration | Medium | An account exists with the same email as the Google identity |
| [email protected] | Signed in to the same account. A second duplicate account is not created. |
Case IDs are positional within this set, not stable identifiers. Import the set, then let your own tool assign its IDs.
How to use this set
Take it as a coverage baseline, not a script. The IDs are stable, so you can reference a case in a bug report and it will still mean the same thing next quarter.
Most of these are worth automating once, because a login page changes rarely and breaks expensively. The three that usually stay manual are the screen reader case, the 400% zoom case and the password manager case: all three are cheap to check by hand and awkward to assert on in code.
What we deliberately left out
Password reset, sign-up and two-factor entry are their own screens with their own failure modes, so they get their own sets rather than a few borrowed rows here. The one reset-related case that stays is the account enumeration check, because the login page is where that leak usually shows up.
Questions about this set
Can I use these in my own project?
Yes. Copy them, edit them, put them in whatever tool you use. No attribution required.
How do I get them into my test management tool?
Use the CSV button above the table. The columns map to the standard title, type, priority, preconditions, steps, test data and expected result fields that TestRail, Qase, Xray and Tesbo all import.
Why are there security and accessibility cases in a functional set?
Because a login page fails in those ways far more often than it fails functionally, and splitting them into a separate document is how they end up never being run.
How current is this?
Every set carries a verified date and the sources it was checked against. Those are at the bottom of the page.
Somewhere to keep these once you have run them
Tesbo holds the cases, the runs and the results in one place, so the next release starts from what the last one proved.