English in the code: fixtures, identifiers, and one phrase table
The fallback mail phrases move into a `Phrases` table, the one place a language other than English appears in this code; the `navn` special case goes (a form names the person's field `name`). Test fixtures speak English: traffic, board, hazards, report, fixed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
713f8ff3a0
commit
e3dae063d4
+92
-61
@@ -741,38 +741,29 @@ pub fn fallback_words(plan: &Plan) -> BTreeMap<String, Value> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// The fallback is true in the plan's language before a model
|
// The fallback is true in the plan's language before a model
|
||||||
// has written a word: Norwegian or English, the two the plans
|
// has written a word; the phrases live in one table below.
|
||||||
// come in.
|
let t = Phrases::for_language(&plan.language);
|
||||||
let no = plan.language.starts_with("no") || plan.language.starts_with("nb") || plan.language.starts_with("nn");
|
let greeting = match need.collects.iter().find(|f| f.name == "name") {
|
||||||
let name_field = need.collects.iter().find(|f| f.name == "name" || f.name == "navn").map(|f| f.name.clone());
|
Some(f) => format!("{} {{{}}},\n\n", t.hello, f.name),
|
||||||
let greeting = match (&name_field, no) {
|
None => format!("{},\n\n", t.hello),
|
||||||
(Some(f), true) => format!("Hei {{{f}}},\n\n"),
|
|
||||||
(None, true) => "Hei,\n\n".to_string(),
|
|
||||||
(Some(f), false) => format!("Hello {{{f}}},\n\n"),
|
|
||||||
(None, false) => "Hello,\n\n".to_string(),
|
|
||||||
};
|
};
|
||||||
let mut per_stage: Map<String, Value> = Map::new();
|
let mut per_stage: Map<String, Value> = Map::new();
|
||||||
for (stage, to) in &need.mails {
|
for (stage, to) in &need.mails {
|
||||||
let stage_word = humanize(stage).to_lowercase();
|
let stage_word = humanize(stage).to_lowercase();
|
||||||
let what = match (stage == &need.stages[0], no) {
|
let what = if stage == &need.stages[0] {
|
||||||
(true, true) => format!("Vi har mottatt det du sendte om {}.", need.wants),
|
format!("{} {}.", t.received_what_you_sent_about, need.wants)
|
||||||
(false, true) => format!("Det du sendte om {} er nå {stage_word}.", need.wants),
|
} else {
|
||||||
(true, false) => format!("We have received what you sent about {}.", need.wants),
|
format!("{} {} {} {stage_word}.", t.what_you_sent_about, need.wants, t.is_now)
|
||||||
(false, false) => format!("What you sent about {} is now {stage_word}.", need.wants),
|
|
||||||
};
|
};
|
||||||
let mut body = if to == SUBMITTER {
|
let mut body = if to == SUBMITTER {
|
||||||
format!("{greeting}{what}")
|
format!("{greeting}{what}")
|
||||||
} else if no {
|
|
||||||
format!("En sak i {} er nå {stage_word}: {{chain}}.", humanize(bucket).to_lowercase())
|
|
||||||
} else {
|
} else {
|
||||||
format!("A case in {} is now {stage_word}: {{chain}}.", humanize(bucket).to_lowercase())
|
format!("{} {} {} {stage_word}: {{chain}}.", t.a_case_in, humanize(bucket).to_lowercase(), t.is_now)
|
||||||
};
|
};
|
||||||
if to == SUBMITTER && moves_of(need, SUBMITTER).iter().any(|(from, _)| from == stage) {
|
if to == SUBMITTER && moves_of(need, SUBMITTER).iter().any(|(from, _)| from == stage) {
|
||||||
let lead = if no { "Det du kan gjøre nå" } else { "What you can do next" };
|
body.push_str(&format!("\n\n{}: {{site}}/decide/{}?chain={{chain}}&email={{email}}", t.what_you_can_do_next, need.id));
|
||||||
body.push_str(&format!("\n\n{lead}: {{site}}/decide/{}?chain={{chain}}&email={{email}}", need.id));
|
|
||||||
}
|
}
|
||||||
let at = if no { "på" } else { "at" };
|
body.push_str(&format!("\n\n{} {} {}", plan.contact.name, t.at, plan.contact.email));
|
||||||
body.push_str(&format!("\n\n{} {at} {}", plan.contact.name, plan.contact.email));
|
|
||||||
per_stage.insert(stage.clone(), json!({ "subject": format!("{}: {}", plan.business, humanize(stage)), "body": body }));
|
per_stage.insert(stage.clone(), json!({ "subject": format!("{}: {}", plan.business, humanize(stage)), "body": body }));
|
||||||
}
|
}
|
||||||
mails.insert(bucket.to_string(), Value::Object(per_stage));
|
mails.insert(bucket.to_string(), Value::Object(per_stage));
|
||||||
@@ -813,6 +804,46 @@ fn decide_path(need: &PlanNeed) -> String {
|
|||||||
|
|
||||||
const SUBMITTER: &str = "submitter";
|
const SUBMITTER: &str = "submitter";
|
||||||
|
|
||||||
|
/// The words of the fallback mails, per language the plans come in.
|
||||||
|
/// The only place a language other than English appears in this
|
||||||
|
/// code: everything else the site says in its own language is a word
|
||||||
|
/// slot the model or a person fills.
|
||||||
|
struct Phrases {
|
||||||
|
hello: &'static str,
|
||||||
|
received_what_you_sent_about: &'static str,
|
||||||
|
what_you_sent_about: &'static str,
|
||||||
|
is_now: &'static str,
|
||||||
|
a_case_in: &'static str,
|
||||||
|
what_you_can_do_next: &'static str,
|
||||||
|
at: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Phrases {
|
||||||
|
fn for_language(lang: &str) -> Phrases {
|
||||||
|
if lang.starts_with("no") || lang.starts_with("nb") || lang.starts_with("nn") {
|
||||||
|
Phrases {
|
||||||
|
hello: "Hei",
|
||||||
|
received_what_you_sent_about: "Vi har mottatt det du sendte om",
|
||||||
|
what_you_sent_about: "Det du sendte om",
|
||||||
|
is_now: "er nå",
|
||||||
|
a_case_in: "En sak i",
|
||||||
|
what_you_can_do_next: "Det du kan gjøre nå",
|
||||||
|
at: "på",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Phrases {
|
||||||
|
hello: "Hello",
|
||||||
|
received_what_you_sent_about: "We have received what you sent about",
|
||||||
|
what_you_sent_about: "What you sent about",
|
||||||
|
is_now: "is now",
|
||||||
|
a_case_in: "A case in",
|
||||||
|
what_you_can_do_next: "What you can do next",
|
||||||
|
at: "at",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Alternatives on one page before a visitor stops reading them.
|
/// Alternatives on one page before a visitor stops reading them.
|
||||||
const MAX_FRONT_DOOR: usize = 7;
|
const MAX_FRONT_DOOR: usize = 7;
|
||||||
|
|
||||||
@@ -1599,9 +1630,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn words_take_the_models_strings_and_never_its_structure() {
|
fn words_take_the_models_strings_and_never_its_structure() {
|
||||||
let fallback = json!({"question": "What?", "needs": {"a": {"heading": "A", "labels": {"email": "Email"}}}});
|
let fallback = json!({"question": "What?", "needs": {"a": {"heading": "A", "labels": {"email": "Email"}}}});
|
||||||
let given = json!({"question": " Hva trenger du? ", "needs": {"a": {"heading": "", "labels": {"email": 7, "extra": "x"}}, "b": {"heading": "B"}}, "injected": "y"});
|
let given = json!({"question": " What do you need? ", "needs": {"a": {"heading": "", "labels": {"email": 7, "extra": "x"}}, "b": {"heading": "B"}}, "injected": "y"});
|
||||||
let merged = merge_words(&fallback, &given);
|
let merged = merge_words(&fallback, &given);
|
||||||
assert_eq!(merged, json!({"question": "Hva trenger du?", "needs": {"a": {"heading": "A", "labels": {"email": "Email"}}}}));
|
assert_eq!(merged, json!({"question": "What do you need?", "needs": {"a": {"heading": "A", "labels": {"email": "Email"}}}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const PLAN: &str = "business: Nordvik Bygg\ncontact: { name: Ingrid, email: i@n.example }\nabout: We fix old buildings.\nneeds:\n - id: survey\n who: owners\n wants: Someone to look at the building\n lands_in: enquiries\n handled_by: project_managers\n stages: [open, surveyed, quoted, won, lost]\n done_when: [won, lost]\n success: [won]\n max_fields: 4\n collects:\n - { name: email, type: email }\n - { name: problem, type: textarea }\n - { name: address, optional: true }\n - id: report\n who: crew\n wants: Report a hazard\n enters_as: crew\n lands_in: site_issues\n handled_by: hse\n stages: [open, in_hand, closed]\n done_when: [closed]\n success: [closed]\n max_fields: 3\n collects:\n - { name: kind, type: select, options: [Hazard, Near miss] }\n";
|
const PLAN: &str = "business: Nordvik Bygg\ncontact: { name: Ingrid, email: i@n.example }\nabout: We fix old buildings.\nneeds:\n - id: survey\n who: owners\n wants: Someone to look at the building\n lands_in: enquiries\n handled_by: project_managers\n stages: [open, surveyed, quoted, won, lost]\n done_when: [won, lost]\n success: [won]\n max_fields: 4\n collects:\n - { name: email, type: email }\n - { name: problem, type: textarea }\n - { name: address, optional: true }\n - id: report\n who: crew\n wants: Report a hazard\n enters_as: crew\n lands_in: site_issues\n handled_by: hse\n stages: [open, in_hand, closed]\n done_when: [closed]\n success: [closed]\n max_fields: 3\n collects:\n - { name: kind, type: select, options: [Hazard, Near miss] }\n";
|
||||||
@@ -1731,42 +1762,42 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn selects_can_take_many_and_draw_their_options_from_the_labelled_groups() {
|
fn selects_can_take_many_and_draw_their_options_from_the_labelled_groups() {
|
||||||
let yaml = "business: B\ncontact: { name: I, email: i@b.example }\ngroups:\n - { id: trafikk, label: Trafikkforhold }\n - { id: elvesti, label: Elvesti }\nneeds:\n - id: komite\n who: en som vil sitte i en komité\n wants: en plass\n lands_in: soknader\n handled_by: styret\n stages: [open, plassert, avslatt]\n done_when: [plassert, avslatt]\n max_fields: 3\n collects:\n - { name: navn }\n - { name: komite, type: select, options_from: groups, multiple: true }\n";
|
let yaml = "business: B\ncontact: { name: I, email: i@b.example }\ngroups:\n - { id: traffic, label: Traffic }\n - { id: elvesti, label: Riverside }\nneeds:\n - id: committee\n who: someone who wants a seat on a committee\n wants: a seat\n lands_in: applications\n handled_by: board\n stages: [open, placed, declined]\n done_when: [placed, declined]\n max_fields: 3\n collects:\n - { name: name }\n - { name: committee, type: select, options_from: groups, multiple: true }\n";
|
||||||
let plan: Plan = serde_yaml::from_str(yaml).unwrap();
|
let plan: Plan = serde_yaml::from_str(yaml).unwrap();
|
||||||
plan.validate().unwrap();
|
plan.validate().unwrap();
|
||||||
let files = render(&plan, &fallback_words(&plan)).unwrap();
|
let files = render(&plan, &fallback_words(&plan)).unwrap();
|
||||||
let page: Value = serde_yaml::from_str(&files["questions/index.yaml"]).unwrap();
|
let page: Value = serde_yaml::from_str(&files["questions/index.yaml"]).unwrap();
|
||||||
let field = &page["alternatives"][1]["features"][2]["requirements"][1];
|
let field = &page["alternatives"][1]["features"][2]["requirements"][1];
|
||||||
assert_eq!(field["options"], json!(["Trafikkforhold", "Elvesti"]));
|
assert_eq!(field["options"], json!(["Traffic", "Riverside"]));
|
||||||
assert_eq!(field["multiple"], true);
|
assert_eq!(field["multiple"], true);
|
||||||
// Without labelled groups there is nothing to draw from.
|
// Without labelled groups there is nothing to draw from.
|
||||||
let no_groups: Plan = serde_yaml::from_str(&yaml.replace("groups:\n - { id: trafikk, label: Trafikkforhold }\n - { id: elvesti, label: Elvesti }\n", "")).unwrap();
|
let no_groups: Plan = serde_yaml::from_str(&yaml.replace("groups:\n - { id: traffic, label: Traffic }\n - { id: elvesti, label: Riverside }\n", "")).unwrap();
|
||||||
assert!(no_groups.validate().unwrap_err().to_string().contains("no labelled"));
|
assert!(no_groups.validate().unwrap_err().to_string().contains("no labelled"));
|
||||||
// `multiple` on a text field is a mistake, not a no-op.
|
// `multiple` on a text field is a mistake, not a no-op.
|
||||||
let bad: Plan = serde_yaml::from_str(&yaml.replace("{ name: navn }", "{ name: navn, multiple: true }")).unwrap();
|
let bad: Plan = serde_yaml::from_str(&yaml.replace("{ name: name }", "{ name: name, multiple: true }")).unwrap();
|
||||||
assert!(bad.validate().unwrap_err().to_string().contains("multiple is for select"));
|
assert!(bad.validate().unwrap_err().to_string().contains("multiple is for select"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_public_watcher_gets_a_read_only_page_without_contact_details() {
|
fn a_public_watcher_gets_a_read_only_page_without_contact_details() {
|
||||||
let yaml = "business: B\ncontact: { name: I, email: i@b.example }\nneeds:\n - id: trafikk\n who: en som har sett noe farlig\n wants: at det tas opp\n lands_in: meldinger\n handled_by: trafikk\n stages: [open, ordnet]\n done_when: [ordnet]\n watched_by: [styret, public]\n max_fields: 2\n collects:\n - { name: hvor }\n - { name: hva, type: textarea }\n - { name: epost, type: email, optional: true }\n - { name: navn, optional: true, private: true }\n";
|
let yaml = "business: B\ncontact: { name: I, email: i@b.example }\nneeds:\n - id: traffic\n who: someone who saw a hazard\n wants: at det tas opp\n lands_in: meldinger\n handled_by: traffic\n stages: [open, fixed]\n done_when: [fixed]\n watched_by: [board, public]\n max_fields: 2\n collects:\n - { name: where }\n - { name: what, type: textarea }\n - { name: epost, type: email, optional: true }\n - { name: name, optional: true, private: true }\n";
|
||||||
let plan: Plan = serde_yaml::from_str(yaml).unwrap();
|
let plan: Plan = serde_yaml::from_str(yaml).unwrap();
|
||||||
plan.validate().unwrap();
|
plan.validate().unwrap();
|
||||||
let files = render(&plan, &fallback_words(&plan)).unwrap();
|
let files = render(&plan, &fallback_words(&plan)).unwrap();
|
||||||
// `public` is not a group: no desk directory for it.
|
// `public` is not a group: no desk directory for it.
|
||||||
assert!(!files.contains_key("questions/public/index.yaml"));
|
assert!(!files.contains_key("questions/public/index.yaml"));
|
||||||
assert!(files.contains_key("questions/styret/index.yaml"));
|
assert!(files.contains_key("questions/board/index.yaml"));
|
||||||
let view: Value = serde_yaml::from_str(&files["questions/status/meldinger/index.yaml"]).unwrap();
|
let view: Value = serde_yaml::from_str(&files["questions/status/meldinger/index.yaml"]).unwrap();
|
||||||
let resource = &view["alternatives"][0]["features"][0]["resource"];
|
let resource = &view["alternatives"][0]["features"][0]["resource"];
|
||||||
assert_eq!(resource["public"], true);
|
assert_eq!(resource["public"], true);
|
||||||
assert!(resource.get("requires_group").is_none());
|
assert!(resource.get("requires_group").is_none());
|
||||||
assert_eq!(resource["jq"], ".[] | .responses |= {hva: .hva, hvor: .hvor}");
|
assert_eq!(resource["jq"], ".[] | .responses |= {what: .what, where: .where}");
|
||||||
assert!(view.get("qualifies").is_none());
|
assert!(view.get("qualifies").is_none());
|
||||||
// The followup page points people at the list.
|
// The followup page points people at the list.
|
||||||
let followup: Value = serde_yaml::from_str(&files["questions/received/trafikk.yaml"]).unwrap();
|
let followup: Value = serde_yaml::from_str(&files["questions/received/traffic.yaml"]).unwrap();
|
||||||
assert_eq!(followup["alternatives"][1]["action"], "/status/meldinger");
|
assert_eq!(followup["alternatives"][1]["action"], "/status/meldinger");
|
||||||
// An internal need cannot be public.
|
// An internal need cannot be public.
|
||||||
let internal: Plan = serde_yaml::from_str(&yaml.replace(" lands_in:", " enters_as: styret\n lands_in:")).unwrap();
|
let internal: Plan = serde_yaml::from_str(&yaml.replace(" lands_in:", " enters_as: board\n lands_in:")).unwrap();
|
||||||
assert!(internal.validate().unwrap_err().to_string().contains("public view"));
|
assert!(internal.validate().unwrap_err().to_string().contains("public view"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2004,26 +2035,26 @@ fn collect_words(a: &serde_yaml::Value, b: &serde_yaml::Value, found: &mut std::
|
|||||||
mod invite_tests {
|
mod invite_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const PLAN: &str = "business: Vel\ncontact: { name: S, email: s@v.example }\ngroups:\n - { id: trafikk, label: Trafikk }\ninvites_by: [styret]\nneeds:\n - id: melde\n who: a neighbour\n wants: a hazard seen to\n lands_in: farer\n handled_by: trafikk\n stages: [open, ordnet]\n done_when: [ordnet]\n max_fields: 3\n collects:\n - { name: hvor }\n - id: utlegg\n who: a committee member\n wants: money back\n enters_as: trafikk\n lands_in: utlegg\n handled_by: styret\n stages: [open, betalt, avvist]\n done_when: [betalt, avvist]\n max_fields: 3\n collects:\n - { name: belop }\n";
|
const PLAN: &str = "business: Vel\ncontact: { name: S, email: s@v.example }\ngroups:\n - { id: traffic, label: Trafikk }\ninvites_by: [board]\nneeds:\n - id: report\n who: a neighbour\n wants: a hazard seen to\n lands_in: hazards\n handled_by: traffic\n stages: [open, fixed]\n done_when: [fixed]\n max_fields: 3\n collects:\n - { name: where }\n - id: expenses\n who: a committee member\n wants: money back\n enters_as: traffic\n lands_in: expenses\n handled_by: board\n stages: [open, betalt, avvist]\n done_when: [betalt, avvist]\n max_fields: 3\n collects:\n - { name: amount }\n";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn words_follow_a_card_that_moves_to_another_page() {
|
fn words_follow_a_card_that_moves_to_another_page() {
|
||||||
// Built once behind a door and hand-worded; rebuilt flat, the
|
// Built once behind a door and hand-worded; rebuilt flat, the
|
||||||
// form's words come along even though its page is gone.
|
// form's words come along even though its page is gone.
|
||||||
let doored = "business: Vel\ncontact: { name: S, email: s@v.example }\ndoors:\n - { id: melde-fra, who: someone who saw something }\nneeds:\n - id: melde\n who: a neighbour\n wants: a hazard seen to\n door: melde-fra\n lands_in: farer\n handled_by: trafikk\n stages: [open, ordnet]\n done_when: [ordnet]\n max_fields: 3\n collects:\n - { name: hvor }\n";
|
let doored = "business: Vel\ncontact: { name: S, email: s@v.example }\ndoors:\n - { id: report-something, who: someone who saw something }\nneeds:\n - id: report\n who: a neighbour\n wants: a hazard seen to\n door: report-something\n lands_in: hazards\n handled_by: traffic\n stages: [open, fixed]\n done_when: [fixed]\n max_fields: 3\n collects:\n - { name: where }\n";
|
||||||
let plan: Plan = serde_yaml::from_str(doored).unwrap();
|
let plan: Plan = serde_yaml::from_str(doored).unwrap();
|
||||||
let mut words = fallback_words(&plan);
|
let mut words = fallback_words(&plan);
|
||||||
let door_page = words.get_mut("questions/melde-fra/index.yaml").unwrap();
|
let door_page = words.get_mut("questions/report-something/index.yaml").unwrap();
|
||||||
door_page["needs"]["melde"]["heading"] = json!("Meld fra om en trafikkfare");
|
door_page["needs"]["report"]["heading"] = json!("Report a traffic hazard");
|
||||||
door_page["needs"]["melde"]["hints"]["hvor"] = json!("Svingen ved skolen");
|
door_page["needs"]["report"]["hints"]["where"] = json!("The bend by the school");
|
||||||
let repo = std::env::temp_dir().join(format!("trainer-lift-{}", std::process::id()));
|
let repo = std::env::temp_dir().join(format!("trainer-lift-{}", std::process::id()));
|
||||||
let _ = std::fs::remove_dir_all(&repo);
|
let _ = std::fs::remove_dir_all(&repo);
|
||||||
write_repo(&repo, render(&plan, &words).unwrap()).unwrap();
|
write_repo(&repo, render(&plan, &words).unwrap()).unwrap();
|
||||||
|
|
||||||
let flat: Plan = serde_yaml::from_str(&doored.replace("doors:\n - { id: melde-fra, who: someone who saw something }\n", "").replace(" door: melde-fra\n", "")).unwrap();
|
let flat: Plan = serde_yaml::from_str(&doored.replace("doors:\n - { id: report-something, who: someone who saw something }\n", "").replace(" door: report-something\n", "")).unwrap();
|
||||||
let lifted = lift_words(&flat, &repo).unwrap();
|
let lifted = lift_words(&flat, &repo).unwrap();
|
||||||
assert_eq!(lifted["questions/index.yaml"]["needs"]["melde"]["heading"], "Meld fra om en trafikkfare");
|
assert_eq!(lifted["questions/index.yaml"]["needs"]["report"]["heading"], "Report a traffic hazard");
|
||||||
assert_eq!(lifted["questions/index.yaml"]["needs"]["melde"]["hints"]["hvor"], "Svingen ved skolen");
|
assert_eq!(lifted["questions/index.yaml"]["needs"]["report"]["hints"]["where"], "The bend by the school");
|
||||||
let _ = std::fs::remove_dir_all(&repo);
|
let _ = std::fs::remove_dir_all(&repo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2032,15 +2063,15 @@ mod invite_tests {
|
|||||||
let plan: Plan = serde_yaml::from_str(PLAN).unwrap();
|
let plan: Plan = serde_yaml::from_str(PLAN).unwrap();
|
||||||
plan.validate().unwrap();
|
plan.validate().unwrap();
|
||||||
let files = render(&plan, &fallback_words(&plan)).unwrap();
|
let files = render(&plan, &fallback_words(&plan)).unwrap();
|
||||||
// The board invites into both groups; trafikk only into itself.
|
// The board invites into both groups; traffic only into itself.
|
||||||
let styret = &files["questions/styret/index.yaml"];
|
let board = &files["questions/board/index.yaml"];
|
||||||
assert_eq!(styret.matches("invite:").count(), 2, "{styret}");
|
assert_eq!(board.matches("invite:").count(), 2, "{board}");
|
||||||
assert!(styret.contains("bucket: portal_invites"));
|
assert!(board.contains("bucket: portal_invites"));
|
||||||
let trafikk = &files["questions/trafikk/index.yaml"];
|
let traffic = &files["questions/traffic/index.yaml"];
|
||||||
assert_eq!(trafikk.matches("invite:").count(), 1);
|
assert_eq!(traffic.matches("invite:").count(), 1);
|
||||||
assert!(trafikk.contains("group: trafikk") && !trafikk.contains("group: styret"));
|
assert!(traffic.contains("group: traffic") && !traffic.contains("group: board"));
|
||||||
// The plan is refused when an inviter has no desk.
|
// The plan is refused when an inviter has no desk.
|
||||||
let bad: Plan = serde_yaml::from_str(&PLAN.replace("invites_by: [styret]", "invites_by: [nobody]")).unwrap();
|
let bad: Plan = serde_yaml::from_str(&PLAN.replace("invites_by: [board]", "invites_by: [nobody]")).unwrap();
|
||||||
assert!(bad.validate().unwrap_err().to_string().contains("no desk"));
|
assert!(bad.validate().unwrap_err().to_string().contains("no desk"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2049,23 +2080,23 @@ mod invite_tests {
|
|||||||
let plan: Plan = serde_yaml::from_str(PLAN).unwrap();
|
let plan: Plan = serde_yaml::from_str(PLAN).unwrap();
|
||||||
let mut words = fallback_words(&plan);
|
let mut words = fallback_words(&plan);
|
||||||
// A hand-edited desk: Norwegian words in the live repo.
|
// A hand-edited desk: Norwegian words in the live repo.
|
||||||
let desk = words.get_mut("questions/trafikk/index.yaml").unwrap();
|
let desk = words.get_mut("questions/traffic/index.yaml").unwrap();
|
||||||
desk["question"] = json!("Hva må gjøres?");
|
desk["question"] = json!("What is waiting?");
|
||||||
desk["buckets"]["farer"]["heading"] = json!("Meldte farer");
|
desk["buckets"]["hazards"]["heading"] = json!("Reported hazards");
|
||||||
desk["buckets"]["farer"]["moves"]["open->ordnet"] = json!("Ordnet");
|
desk["buckets"]["hazards"]["moves"]["open->fixed"] = json!("Fixed");
|
||||||
let dir = std::env::temp_dir().join(format!("lift-{}", std::process::id()));
|
let dir = std::env::temp_dir().join(format!("lift-{}", std::process::id()));
|
||||||
write_repo(&dir, render(&plan, &words).unwrap()).unwrap();
|
write_repo(&dir, render(&plan, &words).unwrap()).unwrap();
|
||||||
|
|
||||||
let lifted = lift_words(&plan, &dir).unwrap();
|
let lifted = lift_words(&plan, &dir).unwrap();
|
||||||
let desk = &lifted["questions/trafikk/index.yaml"];
|
let desk = &lifted["questions/traffic/index.yaml"];
|
||||||
assert_eq!(desk["question"], "Hva må gjøres?");
|
assert_eq!(desk["question"], "What is waiting?");
|
||||||
assert_eq!(desk["buckets"]["farer"]["heading"], "Meldte farer");
|
assert_eq!(desk["buckets"]["hazards"]["heading"], "Reported hazards");
|
||||||
assert_eq!(desk["buckets"]["farer"]["moves"]["open->ordnet"], "Ordnet");
|
assert_eq!(desk["buckets"]["hazards"]["moves"]["open->fixed"], "Fixed");
|
||||||
// A slot the repo never had keeps the fallback.
|
// A slot the repo never had keeps the fallback.
|
||||||
let plan2: Plan = serde_yaml::from_str(&PLAN.replace("invites_by: [styret]", "invites_by: [styret, trafikk]")).unwrap();
|
let plan2: Plan = serde_yaml::from_str(&PLAN.replace("invites_by: [board]", "invites_by: [board, traffic]")).unwrap();
|
||||||
let lifted2 = lift_words(&plan2, &dir).unwrap();
|
let lifted2 = lift_words(&plan2, &dir).unwrap();
|
||||||
assert_eq!(lifted2["questions/trafikk/index.yaml"]["question"], "Hva må gjøres?");
|
assert_eq!(lifted2["questions/traffic/index.yaml"]["question"], "What is waiting?");
|
||||||
assert!(lifted2["questions/trafikk/index.yaml"]["invites"]["styret"]["heading"].as_str().unwrap().starts_with("Invite someone"));
|
assert!(lifted2["questions/traffic/index.yaml"]["invites"]["board"]["heading"].as_str().unwrap().starts_with("Invite someone"));
|
||||||
let _ = std::fs::remove_dir_all(&dir);
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2073,7 +2104,7 @@ mod invite_tests {
|
|||||||
fn words_follow_their_card_when_the_live_page_is_ordered_differently() {
|
fn words_follow_their_card_when_the_live_page_is_ordered_differently() {
|
||||||
let plan: Plan = serde_yaml::from_str(PLAN).unwrap();
|
let plan: Plan = serde_yaml::from_str(PLAN).unwrap();
|
||||||
let mut words = fallback_words(&plan);
|
let mut words = fallback_words(&plan);
|
||||||
words.get_mut("questions/index.yaml").unwrap()["needs"]["melde"]["heading"] = json!("Meld fra om en fare");
|
words.get_mut("questions/index.yaml").unwrap()["needs"]["report"]["heading"] = json!("Report a hazard");
|
||||||
let dir = std::env::temp_dir().join(format!("lift-order-{}", std::process::id()));
|
let dir = std::env::temp_dir().join(format!("lift-order-{}", std::process::id()));
|
||||||
write_repo(&dir, render(&plan, &words).unwrap()).unwrap();
|
write_repo(&dir, render(&plan, &words).unwrap()).unwrap();
|
||||||
// The live front door has an extra first card the plan knows
|
// The live front door has an extra first card the plan knows
|
||||||
@@ -2086,7 +2117,7 @@ mod invite_tests {
|
|||||||
std::fs::write(&front, serde_yaml::to_string(&page).unwrap()).unwrap();
|
std::fs::write(&front, serde_yaml::to_string(&page).unwrap()).unwrap();
|
||||||
|
|
||||||
let lifted = lift_words(&plan, &dir).unwrap();
|
let lifted = lift_words(&plan, &dir).unwrap();
|
||||||
assert_eq!(lifted["questions/index.yaml"]["needs"]["melde"]["heading"], "Meld fra om en fare");
|
assert_eq!(lifted["questions/index.yaml"]["needs"]["report"]["heading"], "Report a hazard");
|
||||||
let _ = std::fs::remove_dir_all(&dir);
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ doors: # a first question before the forms, when more tha
|
|||||||
who: who comes through it # per kind of visitor, two or three needs behind each
|
who: who comes through it # per kind of visitor, two or three needs behind each
|
||||||
|
|
||||||
groups: # groups with a label the public may see: the committees
|
groups: # groups with a label the public may see: the committees
|
||||||
- { id: trafikk, label: Trafikkforhold } # a person can ask to join (optional)
|
- { id: traffic, label: Traffic } # a person can ask to join (optional)
|
||||||
|
|
||||||
needs:
|
needs:
|
||||||
- id: kebab-case # ASCII only
|
- id: kebab-case # ASCII only
|
||||||
|
|||||||
Reference in New Issue
Block a user