Automated Testing of Web App with Selenium

The application calculates a user’s distance to the Sun given their latitude and longitude. It’s at cs458.gahmed.com. I built it test-first: all 19 Selenium test cases existed before a single line of application code was written.

Test Cases

TC1: Invalid Input Handling. Latitude values outside [-90, 90], longitude outside [-180, 180], or incomplete fields should trigger an error message and block calculation. This was the first test I wrote because it defined what the application must reject before I decided what it should accept.

TC2: Input Format Validation. Latitude and longitude fields should only accept numeric characters. Typing “abc” into the latitude field should not result in a silent 0 or NaN propagating through the calculation.

TC3: Button Functionality. Every button on the page performs its labeled action. Trivial to test, surprisingly easy to break during refactors.

TC4: GPS Functionality. Clicking the “use my location” button should populate both fields via the browser’s Geolocation API.

TC5: Responsiveness. Fields and buttons should remain usable at viewport widths corresponding to mobile, tablet, and desktop. Selenium’s driver.set_window_size() handles this.

TC6: Successful Login. Valid credentials produce an authenticated session.

TC7: Invalid Login Handling. Wrong credentials produce an error message. Specifically: an error message, not a silent redirect or a 500 page.

TC8: Login with Enter Key. Pressing Enter in the password field submits the login form. Missing this is one of the most common usability bugs in web forms.

TC9: Email Field Validation. The email field rejects inputs that don’t match an email pattern. “test” should fail; “[email protected]” should pass.

TC10: Empty Field Validation. Submitting the login form with empty fields should produce visible validation messages on both fields.

TC11: Google OAuth Login. The OAuth flow completes and redirects back to the application with an authenticated session.

TC12: UI Element Presence. All expected elements exist on the login page. This test catches the class of bug where a front-end refactor removes an element that the rest of the test suite assumes is there.

TC13: Lat/Lon Fields Presence. Both input fields are present on the distance calculation page before any interaction.

TC14: Calculate Distance Button. The button exists, is clickable, and triggers the calculation.

TC15: Auto-Populated Fields. After GPS access is granted, both fields contain non-empty numeric values.

TC16: Distance Calculation Format. The calculated distance displays as a number followed by units. Not an empty string, not “NaN km”, not a raw float with 15 decimal places.

TC17: Calculate Button Enablement. The button remains enabled when fields are empty but triggers validation messages rather than attempting the calculation. (An alternative design is to disable the button until fields are filled; this test documents the chosen behavior explicitly.)

TC18: GPS Disabled Handling. When the user denies geolocation permission, the application shows a graceful error rather than an unhandled exception or a frozen UI.

TC19: Nearest Sea Section. A secondary feature (nearest sea to the user’s location) is present on the home page.

TDD in Practice

Writing tests before the code forces you to define the API surface of the application before you know how it’s implemented. TC1 required deciding: what counts as “invalid”? What should the error message say? Where should it appear? These are design questions. Answering them in test code before writing the feature means the feature is specced before it’s built.

The early overhead was real. Writing 19 tests before any application code meant I couldn’t run them and see passing output for a while. But every test that failed during development failed with a specific, actionable message because the expected behavior was already written down. Debugging was faster because the test told me exactly what was broken.

The biggest structural benefit: TC12 (UI element presence) caught a regression after a front-end refactor that removed a button ID I’d renamed. It caught it in about 2 seconds. Without that test, the breakage would have surfaced only when someone manually tested that specific flow.

Refactoring and Optimization

With the safety net provided by thorough testing, I confidently engaged in refactoring to enhance design and performance without breaking existing features.

Feel free to check out the Source Code

← back to writing