resolve landing page content

This commit is contained in:
SowinskiBraeden committed 2026-06-29 22:02:04 -07:00
1 parent 35c2b9b4d0
commit 49d9574bf6
2 files changed
+11 -131

No files matched your search

+11 -11
View File
@@ -1,6 +1,6 @@
{% extends "base.html" %}
{% block title %}Free Poker Tracker, Ledger, Stats & Profit/Loss App · myboker.org{% endblock %}
{% block meta_description %}myboker.org, also searched as My Boker, is a free poker tracker and poker ledger for home games: track sessions, buy-ins, cashouts, stats, profits, losses, settlements, and private league bookkeeping.{% endblock %}
{% block meta_description %}myboker.org is a free poker tracker and poker ledger for home games: track sessions, buy-ins, cashouts, stats, profits, losses, settlements, and private league records.{% endblock %}
{% block meta_keywords %}myboker, my boker, free poker tracker, free poker ledger, poker ledger tracker, poker stats tracker, poker profit loss tracker, poker bookkeeping app, home poker tracker, poker league tracker, poker bankroll tracker{% endblock %}
{% block structured_data %}
<script type="application/ld+json">
@@ -63,7 +63,7 @@
"name": "What is My Boker used for?",
"acceptedAnswer": {
"@type": "Answer",
"text": "My Boker, written as myboker.org, is used for home poker bookkeeping: it records poker ledger events, calculates session totals, tracks player wins and losses, and builds league leaderboards."
"text": "myboker.org tracks home poker sessions, buy-ins, cashouts, player results, settlement totals, and league leaderboards."
}
},
{
@@ -89,7 +89,7 @@
<span class="lp-h1__line">tracker</span>
<span class="lp-h1__accent">for home games.</span>
</h1>
<p class="lp-sub">myboker.org, sometimes searched as My Boker, is a free poker ledger for sessions, buy-ins, cashouts, player stats, profit/loss tracking, league leaderboards, and game-night bookkeeping.</p>
<p class="lp-sub">Track buy-ins, cashouts, player stats, profit and loss, settlements, and league leaderboards without rebuilding the same spreadsheet every game night.</p>
<div class="lp-cta">
{% if is_logged_in %}
<a class="btn btn--primary btn--lg" href="{{ url_for('leagues.index') }}">Open my leagues →</a>
@@ -206,7 +206,7 @@
<div class="lp-features__inner">
<div class="lp-sec-head">
<span class="lp-eyebrow">What's included</span>
<h2 class="lp-sec-title">A free poker ledger, stats tracker, and league bookkeeper.</h2>
<h2 class="lp-sec-title">A free poker ledger and stats tracker for home games.</h2>
<p class="lp-sec-sub">Everything your group needs to run a real poker league, built from the ground up for home games.</p>
</div>
<div class="lp-feat-grid">
@@ -239,8 +239,8 @@
<path d="M7 7h6M7 10.5h6M7 14h3.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
</div>
<h3 class="lp-feat__title">Poker ledger and bookkeeping</h3>
<p class="lp-feat__desc">Every event is logged and kept: buy-ins, cashouts, fronts, corrections, profits, and losses. Nothing is deleted; mistakes get fixed by appending. When a session closes, the house knows exactly what it holds and who is owed what.</p>
<h3 class="lp-feat__title">Clean records for every night</h3>
<p class="lp-feat__desc">Every event is logged and kept: buy-ins, cashouts, fronts, corrections, profits, and losses. Mistakes get fixed by appending a new entry, so the session history stays intact and settlement stays clear.</p>
</div>
</div>
</div>
@@ -472,24 +472,24 @@
<div class="lp-sec-head">
<span class="lp-eyebrow">Free poker tracker</span>
<h2 class="lp-sec-title">Poker records without a spreadsheet.</h2>
<p class="lp-sec-sub">myboker.org is a poker ledger tracker for home games that need simple, reliable records for money in, money out, and every player's results.</p>
<p class="lp-sec-sub">Keep reliable records for money in, money out, and every player's result without turning game night into admin work.</p>
</div>
<div class="lp-seo__grid">
<article class="lp-seo__item">
<h3>Track poker profits and losses</h3>
<p>Record each buy-in, re-buy, cashout, correction, and note so every player's poker profit or loss is calculated from the source ledger instead of copied into a spreadsheet later.</p>
<p>Record each buy-in, re-buy, cashout, correction, and note as it happens. Player profit and loss updates from the ledger automatically.</p>
</article>
<article class="lp-seo__item">
<h3>Keep a clean poker ledger</h3>
<p>The append-only ledger gives your home game an auditable bookkeeping trail for sessions, settlements, fronts, and payouts while preserving the history behind every total.</p>
<p>The append-only ledger keeps sessions, settlements, fronts, and payouts in order while preserving the history behind every total.</p>
</article>
<article class="lp-seo__item">
<h3>Build poker stats automatically</h3>
<p>League leaderboards, net winnings, ROI, win rate, session counts, recent form, and rank movement update from the same data your group enters on game night.</p>
</article>
<article class="lp-seo__item">
<h3>Find My Boker by name</h3>
<p>The product is myboker.org, also written as My Boker or my boker. It is built for poker tracking, not insurance, brokerage, or general finance workflows.</p>
<h3>Built for home games</h3>
<p>Private leagues, shared results, and public league pages are all optional. Your group decides what stays private and what gets shared.</p>
</article>
</div>
</div>
-120
View File
@@ -1,120 +0,0 @@
import tempfile
import unittest
from pathlib import Path
from app import create_app
from boker.auth import hash_password
from boker.db import db
class SeoTests(unittest.TestCase):
def setUp(self):
self.tmpdir = tempfile.TemporaryDirectory()
db_path = Path(self.tmpdir.name) / "test.sqlite3"
self.app = create_app(
{
"TESTING": True,
"APP_BASE_URL": "https://myboker.org",
"SQLALCHEMY_DATABASE_URI": f"sqlite:///{db_path}",
}
)
self.client = self.app.test_client()
with self.app.app_context():
db.create_all()
def tearDown(self):
with self.app.app_context():
db.session.remove()
db.drop_all()
db.engine.dispose()
self.tmpdir.cleanup()
def test_landing_page_has_search_metadata(self):
response = self.client.get("/")
html = response.get_data(as_text=True)
self.assertEqual(response.status_code, 200)
self.assertIn("Free Poker Tracker, Ledger, Stats & Profit/Loss App", html)
self.assertIn('name="description"', html)
self.assertIn("free poker tracker and poker ledger for home games", html)
self.assertIn("My Boker", html)
self.assertIn("poker profit or loss", html)
self.assertIn('rel="canonical" href="https://myboker.org/"', html)
self.assertIn('application/ld+json', html)
def test_robots_txt_allows_public_crawling_and_points_to_sitemap(self):
response = self.client.get("/robots.txt")
body = response.get_data(as_text=True)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.mimetype, "text/plain")
self.assertIn("Allow: /", body)
self.assertIn("Disallow: /internal/", body)
self.assertIn("Disallow: /account/", body)
self.assertIn("Sitemap: https://myboker.org/sitemap.xml", body)
def test_sitemap_xml_lists_public_static_pages(self):
response = self.client.get("/sitemap.xml")
body = response.get_data(as_text=True)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.mimetype, "application/xml")
self.assertIn("<loc>https://myboker.org/</loc>", body)
self.assertIn("<loc>https://myboker.org/explore</loc>", body)
self.assertIn("<loc>https://myboker.org/help</loc>", body)
def test_explore_requires_search_before_showing_public_leagues(self):
with self.app.app_context():
from boker.db_models import League, User
owner = User(email="owner@example.com", password_hash=hash_password("password123"))
db.session.add(owner)
db.session.flush()
league = League(
name="Friday Night Poker",
slug="friday-night-poker",
public_key="friday1",
created_by_user_id=owner.id,
visibility="public",
)
db.session.add(league)
db.session.commit()
default_response = self.client.get("/explore")
default_html = default_response.get_data(as_text=True)
search_response = self.client.get("/explore?q=Friday")
search_html = search_response.get_data(as_text=True)
sitemap_response = self.client.get("/sitemap.xml")
sitemap_xml = sitemap_response.get_data(as_text=True)
self.assertEqual(default_response.status_code, 200)
self.assertIn("Enter a league name to search", default_html)
self.assertNotIn("Friday Night Poker", default_html)
self.assertEqual(search_response.status_code, 200)
self.assertIn("Friday Night Poker", search_html)
self.assertNotIn("/l/friday-night-poker-friday1", sitemap_xml)
def test_explore_search_handles_missing_database_tables(self):
with tempfile.TemporaryDirectory() as tmpdir:
db_path = Path(tmpdir) / "missing-tables.sqlite3"
app = create_app(
{
"TESTING": True,
"SQLALCHEMY_DATABASE_URI": f"sqlite:///{db_path}",
}
)
client = app.test_client()
response = client.get("/explore?q=Friday")
with app.app_context():
db.session.remove()
db.engine.dispose()
html = response.get_data(as_text=True)
self.assertEqual(response.status_code, 200)
self.assertIn("No public leagues found", html)
if __name__ == "__main__":
unittest.main()