2026-05-16 23:03:50 +00:00
|
|
|
package app
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"io"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"net/url"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestPublicRoutes(t *testing.T) {
|
|
|
|
|
srv := newTestServer(t)
|
|
|
|
|
handler := srv.Routes()
|
|
|
|
|
|
2026-05-17 12:36:50 +00:00
|
|
|
for _, path := range []string{"/", "/projects", "/about", "/services", "/contact", "/projects/courtyard-house"} {
|
2026-05-16 23:03:50 +00:00
|
|
|
req := httptest.NewRequest(http.MethodGet, path, nil)
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("%s returned %d", path, rec.Code)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 12:36:50 +00:00
|
|
|
func TestServicesRouteRendersServicesAndFAQs(t *testing.T) {
|
|
|
|
|
srv := newTestServer(t)
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/services", nil)
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
|
|
srv.Routes().ServeHTTP(rec, req)
|
|
|
|
|
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("expected ok, got %d", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
body, _ := io.ReadAll(rec.Result().Body)
|
|
|
|
|
text := string(body)
|
|
|
|
|
for _, want := range []string{"Residential architecture", "How projects work", "FAQs", "Start an enquiry"} {
|
|
|
|
|
if !strings.Contains(text, want) {
|
|
|
|
|
t.Fatalf("services page missing %q: %s", want, text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestHomeRendersPhaseTwoSections(t *testing.T) {
|
|
|
|
|
srv := newTestServer(t)
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
|
|
srv.Routes().ServeHTTP(rec, req)
|
|
|
|
|
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("expected ok, got %d", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
body, _ := io.ReadAll(rec.Result().Body)
|
|
|
|
|
text := string(body)
|
|
|
|
|
for _, want := range []string{"Start an enquiry", "Focused support", "Process", "Studio profile"} {
|
|
|
|
|
if !strings.Contains(text, want) {
|
|
|
|
|
t.Fatalf("home missing %q: %s", want, text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestStudioRendersExpandedContent(t *testing.T) {
|
|
|
|
|
srv := newTestServer(t)
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/about", nil)
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
|
|
srv.Routes().ServeHTTP(rec, req)
|
|
|
|
|
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("expected ok, got %d", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
body, _ := io.ReadAll(rec.Result().Body)
|
|
|
|
|
text := string(body)
|
|
|
|
|
for _, want := range []string{"Philosophy", "Approach", "Experience", "Service area"} {
|
|
|
|
|
if !strings.Contains(text, want) {
|
|
|
|
|
t.Fatalf("studio missing %q: %s", want, text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-16 23:03:50 +00:00
|
|
|
func TestContactSubmissionPersists(t *testing.T) {
|
|
|
|
|
srv := newTestServer(t)
|
2026-05-17 12:36:50 +00:00
|
|
|
form := url.Values{
|
|
|
|
|
"name": {"Jane"},
|
|
|
|
|
"email": {"jane@example.com"},
|
|
|
|
|
"phone": {"123"},
|
|
|
|
|
"project_type": {"Renovation or extension"},
|
|
|
|
|
"project_location": {"London"},
|
|
|
|
|
"budget_range": {"GBP 250k-500k"},
|
|
|
|
|
"timeline": {"3-6 months"},
|
|
|
|
|
"message": {"New project"},
|
|
|
|
|
}
|
2026-05-16 23:03:50 +00:00
|
|
|
req := httptest.NewRequest(http.MethodPost, "/contact", strings.NewReader(form.Encode()))
|
|
|
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
|
|
srv.Routes().ServeHTTP(rec, req)
|
|
|
|
|
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("expected ok, got %d", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
body, _ := io.ReadAll(rec.Result().Body)
|
|
|
|
|
if !strings.Contains(string(body), "saved") {
|
|
|
|
|
t.Fatalf("expected success message, got %s", body)
|
|
|
|
|
}
|
|
|
|
|
requests, err := srv.store.ContactRequests(req.Context())
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2026-05-17 12:36:50 +00:00
|
|
|
if len(requests) != 1 || requests[0].Email != "jane@example.com" || requests[0].ProjectType != "Renovation or extension" || requests[0].Status != "new" {
|
2026-05-16 23:03:50 +00:00
|
|
|
t.Fatalf("unexpected contact requests: %+v", requests)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 12:36:50 +00:00
|
|
|
func TestContactSubmissionRequiresQualificationFields(t *testing.T) {
|
|
|
|
|
srv := newTestServer(t)
|
|
|
|
|
form := url.Values{"name": {"Jane"}, "email": {"jane@example.com"}, "message": {"New project"}}
|
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/contact", strings.NewReader(form.Encode()))
|
|
|
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
|
|
srv.Routes().ServeHTTP(rec, req)
|
|
|
|
|
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("expected ok, got %d", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
body, _ := io.ReadAll(rec.Result().Body)
|
|
|
|
|
if !strings.Contains(string(body), "project type is required") {
|
|
|
|
|
t.Fatalf("expected qualification validation message, got %s", body)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-16 23:03:50 +00:00
|
|
|
func TestProjectImageOverlay(t *testing.T) {
|
|
|
|
|
srv := newTestServer(t)
|
|
|
|
|
project, err := srv.store.ProjectBySlug(t.Context(), "courtyard-house")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if len(project.Images) == 0 {
|
|
|
|
|
t.Fatal("expected seeded project images")
|
|
|
|
|
}
|
|
|
|
|
path := "/projects/" + project.Slug + "/images/" + strconv.FormatInt(project.Images[0].ID, 10) + "/overlay"
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, path, nil)
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
|
|
srv.Routes().ServeHTTP(rec, req)
|
|
|
|
|
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("expected ok, got %d", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
body, _ := io.ReadAll(rec.Result().Body)
|
|
|
|
|
if !strings.Contains(string(body), "data-overlay") || !strings.Contains(string(body), project.Images[0].Path) {
|
|
|
|
|
t.Fatalf("overlay fragment missing expected content: %s", body)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-17 12:36:50 +00:00
|
|
|
|
|
|
|
|
func TestProjectDetailRendersDepthFields(t *testing.T) {
|
|
|
|
|
srv := newTestServer(t)
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/projects/courtyard-house", nil)
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
|
|
srv.Routes().ServeHTTP(rec, req)
|
|
|
|
|
|
|
|
|
|
if rec.Code != http.StatusOK {
|
|
|
|
|
t.Fatalf("expected ok, got %d", rec.Code)
|
|
|
|
|
}
|
|
|
|
|
body, _ := io.ReadAll(rec.Result().Body)
|
|
|
|
|
text := string(body)
|
|
|
|
|
for _, want := range []string{"Scope", "Completed", "Architecture, interiors, material strategy", "A private house arranged around a quiet internal garden."} {
|
|
|
|
|
if !strings.Contains(text, want) {
|
|
|
|
|
t.Fatalf("project detail missing %q: %s", want, text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|