NestFinder is a web app that helps students find housing near UBC. It brings listings together in one place so you can search, filter, and compare homes — by number of bedrooms, monthly budget, and distance from campus — instead of hopping between different marketplaces.
The app runs entirely in the browser (HTML, CSS, and vanilla JavaScript). It needs no database or backend server — accounts and listings are handled on the client side, so it's easy to run locally.
- Account registration & login — create an account and sign in. Accounts
are stored in the browser (
localStorage); passwords are hashed before being saved, never stored in plain text. - Auth-aware navigation — the homepage buttons know whether you're signed in: logged-in users go straight to browsing, logged-out users are sent to register/login.
- Login-gated browsing — the Browse page is available to signed-in users only; visiting it while logged out redirects to the login page.
- Combined browse experience — one page shows the "Browse NestFinder" banner, keyword search, filter settings, and the full list of homes.
- Keyword search — search listings by title or type (apartment, house, room).
- Filters — narrow results by:
- Number of bedrooms (at least 1 / 2 / 3)
- Monthly budget (under $2000 / $2500 / $3000)
- Distance from UBC (within 0.5 / 1 / 1.5 / 2 / 2.5 km)
- Filters combine, show a live result count, and can be cleared in one click.
- Per-listing detail pages — each home opens its own detail view with a photo, description, price, distance, external listing link, and a "More Listings" strip.
- Navbar tools — a notification dropdown (for signed-in users), a user profile dropdown showing your account, and a convenient log in / log out button.
- FAQ — an accordion on the homepage answering common questions.
Pages live in pages/, scripts in js/, styles in css/, and artwork in
icons/ and images/:
nest-finder/
├── index.html Redirect: sends the server root to pages/homepage.html
├── pages/ HTML pages
├── js/ Browser scripts
├── css/ Stylesheet
├── icons/ Logos and UI icons
├── images/ Listing and banner photos
├── test/ Automated tests
├── serve.py Local no-cache static server
└── start.sh Launcher (serves the site, opens the homepage)
| File | Purpose |
|---|---|
index.html |
Redirects the server root to pages/homepage.html |
pages/homepage.html |
Landing page: intro, auth-aware buttons, FAQ |
pages/login.html |
Sign in to an existing account |
pages/register.html |
Create a new account |
pages/browsepage.html |
Browse page (login required): banner, search, filters, listing grid |
pages/housepage.html |
House detail view (housepage.html?id=<id>) |
js/auth.js |
Client-side authentication (register / login / logout / session) |
js/navbar-auth.js |
Navbar behaviour: notification & profile dropdowns, login/logout button |
js/listings.js |
Housing listing data |
js/results.js |
Renders the browse grid and drives search + filtering |
js/house.js |
Renders the house detail page and "More Listings" |
css/style.css |
Site-wide styles |
serve.py |
Small no-cache static file server for local development |
start.sh |
Convenience launcher — starts the server and opens the homepage |
test/ |
Automated tests (see Testing) |
Pages link to each other by bare filename (they share the pages/ folder) and
reach everything else with ../ — so js/listings.js stores photo paths like
../images/result-img-1.png, the way the page that renders them resolves it.
Requirements: Python 3 (pre-installed on macOS) and a web browser.
From the project folder, run:
./start.shThis starts a local server and opens http://localhost:8000/pages/homepage.html
in your browser. Press Ctrl+C to stop it. Plain http://localhost:8000/ works
too — the root index.html redirects there.
To use a different port:
./start.sh 3000Or start the server directly without auto-opening a browser:
python3 serve.py 8000Tip: run the app through the server (
http://localhost) rather than opening the HTML files directly (file://). The server sends no-cache headers, so your latest changes always show up without a hard refresh.
The site itself needs no build step, but the tests run on Node (18+) with Vitest and jsdom.
Install the dev dependencies once:
npm installThen run the suite:
npm testOther useful commands:
npm run test:watchnpm run coveragenpm run coverage prints a summary and writes a browsable HTML report to
coverage/index.html.
The site's scripts are plain <script> files rather than modules, so the tests
reproduce a browser page instead of importing functions:
- The real page markup is loaded from
pages/browsepage.html/pages/housepage.html(with its<script>tags stripped) into a jsdom document — so a renamed class or id in the HTML fails the tests rather than silently breaking the site. test/helpers/page.jsevaluates the site's scripts in page order and then firesDOMContentLoaded, the same lifecycle a browser gives them. Each load is a fresh evaluation, so filter state never leaks between tests.test/setup/storage.jssupplies an in-memorylocalStorage, since accounts and sessions live there.
| Test file | Covers |
|---|---|
test/auth.test.js |
Registration rules, login, sessions, password hashing |
test/results.test.js |
Browse grid rendering, keyword search, filters, URL parameters, clearing |
test/house.test.js |
Detail page population, ?id= lookup and fallback, "More Listings" |
test/navbar-auth.test.js |
Notification and profile dropdowns, log in / log out controls |
test/listings.test.js |
Shape and integrity of the listing data |
- Accounts are per-browser. Because data lives in
localStorage, an account you register in one browser or device won't exist in another, and clearing browser data removes it. There is no shared user database. - Demo-grade security. The password hash is a simple, non-cryptographic hash suitable for a prototype — not for production use.
- Listings are sample data defined in
js/listings.js. Making listings shared and persistent across devices would require a real backend with a database.