forked from Mike/freno-dev
Merge branch 'NOOK-334' — NOOK-334 landing page feature set
This commit is contained in:
@@ -5,6 +5,7 @@ import { describe, expect, it, mock, beforeEach } from "bun:test";
|
||||
// and description, HTML-escaped.
|
||||
// 2. Invalid payloads never reach the mailer.
|
||||
// 3. The rate limiter halts floods per fingerprint (falling back to IP).
|
||||
// 4. The landing page's iPhone beta request (email only) gets through.
|
||||
// sendEmail and the limiter are mocked so the test sends no real mail.
|
||||
|
||||
const sentEmails: { to: string; subject: string; html: string }[] = [];
|
||||
@@ -22,6 +23,9 @@ mock.module("~/server/bug-report-rate-limit", () => ({
|
||||
}));
|
||||
|
||||
const { POST } = await import("./report");
|
||||
const { betaRequestPayload } = await import(
|
||||
"~/components/nook/betaRequest"
|
||||
);
|
||||
|
||||
/** A minimal APIEvent double: JSON body plus optional headers. */
|
||||
function request(body: unknown, headers: Record<string, string> = {}) {
|
||||
@@ -134,4 +138,17 @@ describe("POST /api/the-nook/report", () => {
|
||||
expect(res.status).toBe(200);
|
||||
expect(sentEmails).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("delivers the landing page's beta request with only the email", async () => {
|
||||
const res = await POST(request(betaRequestPayload("tester@example.com")));
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
expect(sentEmails).toHaveLength(1);
|
||||
const email = sentEmails[0]!;
|
||||
expect(email.subject).toBe(
|
||||
"The Nook bug report: iPhone app beta access request"
|
||||
);
|
||||
expect(email.html).toContain("<b>Reply-to:</b> tester@example.com");
|
||||
expect(email.html).not.toContain("<h3>Displays</h3>");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,8 @@ import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
|
||||
import SubdomainHeader from "~/components/SubdomainHeader";
|
||||
import FeatureCollage from "~/components/nook/FeatureCollage";
|
||||
import FeatureBreakdowns from "~/components/nook/FeatureBreakdowns";
|
||||
import IslandExtras from "~/components/nook/IslandExtras";
|
||||
import PhoneBeta from "~/components/nook/PhoneBeta";
|
||||
import Button from "~/components/ui/Button";
|
||||
import Input from "~/components/ui/Input";
|
||||
import { createSignal, Show } from "solid-js";
|
||||
@@ -149,6 +151,12 @@ export default function NookLanding() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Everything else: marquee + live module bento ───────────── */}
|
||||
<IslandExtras />
|
||||
|
||||
{/* ── iPhone + Watch companion, TestFlight beta ──────────────── */}
|
||||
<PhoneBeta brandColor={brandColor()} />
|
||||
|
||||
{/* ── CTA ──────────────────────────────────────────────────────── */}
|
||||
<section class="bg-surface0 relative z-20 px-4 py-20 md:px-8">
|
||||
<div class="mx-auto max-w-4xl text-center">
|
||||
@@ -156,10 +164,10 @@ export default function NookLanding() {
|
||||
Try it free for 14 days
|
||||
</h2>
|
||||
<p class="text-subtext0 mx-auto mb-10 max-w-2xl leading-relaxed">
|
||||
Orchestrate your coding agents, watch temperatures, take control of
|
||||
your fans, and keep background services quiet. When the trial ends,
|
||||
unlock everything with a single one-time payment — no subscription,
|
||||
ever.
|
||||
Orchestrate your coding agents, take control of your fans and
|
||||
battery, give every app its own volume, capture your screen, and
|
||||
keep your day in the notch. When the trial ends, unlock everything
|
||||
with a single one-time payment — no subscription, ever.
|
||||
</p>
|
||||
<div class="flex flex-col items-center justify-center gap-4 sm:flex-row sm:space-x-4">
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user