,
may_pass: bool,
log: &[LogLine],
meta: &[String],
) -> String {
+ let (endpoint, note_to) = (to.command, to.note);
let mut s = String::with_capacity(8192);
let _ = write!(
s,
@@ -743,7 +768,7 @@ pub fn document_with_log(
body(&mut s, view);
move_section(&mut s, legal, seat, may_pass);
s.push_str("");
- meta_section(&mut s, meta);
+ meta_section(&mut s, meta, note_to);
log_section(&mut s, log);
s.push_str("
");
let _ = write!(
@@ -761,7 +786,7 @@ pub fn document_with_log(
///
/// Empty is a legitimate state — a first game has no tally and may have no
/// notes — and renders as nothing rather than as an empty heading.
-fn meta_section(s: &mut String, meta: &[String]) {
+fn meta_section(s: &mut String, meta: &[String], note_to: &str) {
// CB-WP-0027 T03: the comment box. Always present — the panel's
// purpose is that a player can say something at any moment, and a box
// that appears only sometimes trains them not to look for it.
@@ -770,12 +795,14 @@ fn meta_section(s: &mut String, meta: &[String]) {
// The command channel needs JavaScript because a drag is not a form
// submission; a comment is, and making it depend on the script would
// add a failure mode for no gain.
- s.push_str(
+ let _ = write!(
+ s,
"what are you thinking?
\
- ",
+ note_to = esc(note_to),
);
if meta.is_empty() {
return;
diff --git a/crates/cb-render-html/src/lib.rs b/crates/cb-render-html/src/lib.rs
index a460ab4..2885d15 100644
--- a/crates/cb-render-html/src/lib.rs
+++ b/crates/cb-render-html/src/lib.rs
@@ -47,7 +47,14 @@ pub mod input;
pub mod jsrun;
pub mod serve;
-pub use doc::{document, text_of};
+pub use doc::{document, text_of, Endpoints};
+
+/// The endpoint pair every test in this crate posts to.
+#[cfg(test)]
+const TEST_ENDPOINTS: Endpoints<'static> = Endpoints {
+ command: "/command?t=x",
+ note: "/note?t=x",
+};
pub use input::{resolve, Note, PointerFact};
pub use serve::{Guard, Refusal, Request};
@@ -186,7 +193,7 @@ mod coverage {
text_of(&document(
view,
&[],
- "/command?t=x",
+ crate::TEST_ENDPOINTS.command,
Some(PlayerId(0)),
false,
))
@@ -595,7 +602,7 @@ mod gamelog {
document_with_log(
&crate::testfix::view(Some(PlayerId(0))),
&[],
- "/command?t=x",
+ crate::TEST_ENDPOINTS,
Some(PlayerId(0)),
false,
log,
@@ -779,7 +786,7 @@ mod notes {
let html = document_with_log(
&crate::testfix::view(Some(PlayerId(0))),
&[],
- "/command?t=x",
+ crate::TEST_ENDPOINTS,
Some(PlayerId(0)),
false,
&[],
@@ -807,13 +814,16 @@ mod notes {
let html = document_with_log(
&crate::testfix::view(Some(PlayerId(0))),
&[],
- "/command?t=x",
+ crate::TEST_ENDPOINTS,
Some(PlayerId(0)),
false,
&[],
&[],
);
- assert!(html.contains("action=\"/note\""), "no comment box");
+ assert!(
+ html.contains("action=\"/note?t=x\""),
+ "the form must carry the session token"
+ );
assert!(
html.contains("method=\"post\""),
"a plain form, so it works with the script disabled"
@@ -832,7 +842,7 @@ mod two_columns {
document_with_log(
&crate::testfix::view(Some(PlayerId(0))),
&[],
- "/command?t=x",
+ crate::TEST_ENDPOINTS,
Some(PlayerId(0)),
false,
&[],
diff --git a/crates/cb-render-html/src/serve.rs b/crates/cb-render-html/src/serve.rs
index dfe464d..e4f3169 100644
--- a/crates/cb-render-html/src/serve.rs
+++ b/crates/cb-render-html/src/serve.rs
@@ -158,6 +158,17 @@ impl Guard {
format!("/command?t={}", self.token)
}
+ /// The note channel's endpoint, token and all (CB-WP-0027).
+ ///
+ /// **A form's `action` must carry the token like every other
+ /// request.** The first version posted to a bare `/note` and was
+ /// refused with "no session token" — the endpoint had been tested
+ /// with a token appended by hand, so the test exercised the mechanism
+ /// and not the path a player takes.
+ pub fn note_endpoint(&self) -> String {
+ format!("/note?t={}", self.token)
+ }
+
pub fn page_url(&self) -> String {
format!("{}/?t={}", self.origin, self.token)
}
diff --git a/tools/cb-play/src/hotseat.rs b/tools/cb-play/src/hotseat.rs
index d853c3f..3513935 100644
--- a/tools/cb-play/src/hotseat.rs
+++ b/tools/cb-play/src/hotseat.rs
@@ -243,7 +243,10 @@ impl Server {
let page = cb_render_html::doc::document_with_log(
&view,
legal,
- &self.guard.endpoint(),
+ cb_render_html::Endpoints {
+ command: &self.guard.endpoint(),
+ note: &self.guard.note_endpoint(),
+ },
Some(seat),
may_pass,
&self.log_lines(),