DEV Community

Cover image for Day 96 of #100DaysOfCode — DevCollab: Responsive Design and End-to-End Testing
M Saad Ahmad
M Saad Ahmad

Posted on

Day 96 of #100DaysOfCode — DevCollab: Responsive Design and End-to-End Testing

Two days until deployment. Today was the last day of building work, making the app work on mobile, and running every user flow from start to finish to find and fix anything broken before it goes live. Not glamorous work, but necessary work. The things found during testing today would have been embarrassing to discover after deploying.


Responsive Design

The app was built desktop-first. Every layout decision: two-column grids, side-by-side forms, horizontal navigation, was made with a 1200px viewport in mind. Opening the browser dev tools and switching to a 375px mobile viewport revealed exactly what I expected: broken layouts, overflowing text, buttons too small to tap, and a Navbar that disappeared off the right side of the screen.

Tailwind's responsive prefixes make fixing this systematic rather than guesswork. Going page by page and applying sm:, md:, and lg: prefixes to the relevant classes turned the desktop layout into a responsive one. The pattern is almost always the same: a two-column grid becomes a single column, a side-by-side form becomes stacked fields, a horizontal stat row becomes a two-by-two grid, and horizontal button groups become full-width stacked buttons.

The pages that needed the most work were the project detail page, the two-column main content and sidebar layout, the dashboard, the four-column stats row, and the project rows with action buttons on the right. Both collapsed to a single column on mobile, with the action buttons moving below the content they relate to.

The browse page search bar was the trickiest. Three inputs and two buttons in a horizontal row on a desktop becomes a mess on mobile. The solution was stacking them vertically on small screens, with each input taking full width. The filter button becomes full-width, too. Functionally identical, just arranged differently.


The Navbar Mobile Menu

The desktop Navbar has five or six items, depending on the auth state. On a 375px screen, that's an immediate overflow. I added a hamburger menu button that only shows on small screens and toggles a dropdown panel below the Navbar containing all the same links.

The hamburger button uses Tailwind's md:hidden to hide on medium and larger screens. The full navigation links use hidden md:flex to show only on medium and larger. On mobile, the dropdown panel slides down from the Navbar when the hamburger is clicked and closes when any link is selected.

The dropdown has the same background and border as the Navbar and sits on top of the page content with a high z-index. Each link in the dropdown takes the full width and has enough vertical padding to be easily tappable with a finger.


End-to-End Testing

With responsive design done, I ran through every user flow in the browser, not with Postman, not in the admin panel, just the browser from the perspective of a real user. This is the most valuable testing you can do before deployment because it catches the class of bugs that only appear when the full stack is connected, and a real person is navigating through it.

The flows I tested in order:

New user registration and onboarding. Register with a new username and email, get redirected to the dashboard, see the empty state, click through to edit the profile, fill in skills and bio, save, and see the public profile correctly display the new data.

Posting a project. Create a new project with all fields filled in, get redirected to the detail page, confirm the tech stack and roles render as tags, confirm the Edit and Delete buttons show since this is the owner's project.

Browsing and applying. Log out. Log in as a different user. Browse projects. Use the search bar to filter. Click into a project. Confirm that the Request to Collaborate button shows. Click it, write a message, submit. Get redirected to the detail page, confirm that the pending badge shows. Navigate to sent requests and confirm the request appears with a pending status.

Request management. Switch back to the project owner account. Open the dashboard. Confirm the incoming request appears. Accept it. Confirm the status badge updates are in place without a page reload. Switch back to the requester account, check sent requests, and confirm the status shows accepted.

Permission testing. While logged out, try navigating directly to /dashboard, confirm redirect to login. Try navigating to /projects/new, confirm redirect. Log in as a user who doesn't own a project and try navigating directly to /projects/[id]/edit, confirm redirect back to the detail page.

Profile and public pages. Visit another user's public profile by username. Confirm projects show. Confirm that no edit button is visible since it's not your profile. Try a username that doesn't exist and confirm the not-found page shows.


Bugs Found and Fixed

Testing always finds things. Today found five.

The first was on the apply page. If a user navigated directly to /projects/[id]/apply for a project that didn't exist, the page threw a JavaScript error because it tried to access properties on a null project object before the not-found check ran. The fix was adding a loading guard so the form only renders once the project data exists.

The second was on the dashboard. When all incoming requests were accepted or rejected, and none were pending, the requests section showed nothing, not even a message. The empty state component wasn't being rendered in the right condition. Fixed by adjusting the conditional check.

The third was the edit project form, not initialising the is_open toggle correctly. The form was treating the boolean false from the API as a missing value and defaulting to true, making every project appear open when editing. Fixed by using ?? true instead of || true, the nullish coalescing operator correctly handles false, whereas logical OR converts it to the default.

The fourth was a CORS issue discovered when testing with the actual Django server running. The Next.js dev server runs on port 3000, but I'd only added http://localhost:3000 to CORS_ALLOWED_ORIGINS. Adding the variant without a trailing slash fixed it.

The fifth was the Navbar not updating its display immediately after logout. The user state was being cleared, but the Navbar component wasn't re-rendering. Fixed by ensuring the logout function updates the auth context state synchronously before the redirect.


Django Deployment Checklist

With the frontend testing done, I switched to the backend and ran python manage.py check --deploy. It flagged four things.

DEBUG = True — expected, will be set to False in the production environment.

SECRET_KEY too predictable — the key in .env for local development was too short. Generated a proper 50-character random key for production.

Missing SECURE_SSL_REDIRECT — will be set in production settings.

Missing SESSION_COOKIE_SECURE and CSRF_COOKIE_SECURE — both will be set in production settings.

None of these are surprises. All of them are standard settings that are intentionally different in development and production. The production settings file — which reads from environment variables — will have all of these set correctly when deployed to Railway.

WhiteNoise was installed and configured to serve Django's static files in production. Without it, Django requires a separate web server to serve static files — WhiteNoise handles them directly from the Django process, which is simpler for a deployment this size.


Where Things Stand After Day 96

The app works on mobile. Every user flow passes end-to-end testing. Five bugs were found and fixed. The Django backend passes the deployment check with known production settings flagged. WhiteNoise is configured.

Thanks for reading. Feel free to share your thoughts!

Top comments (0)