Testing plan wave 0: close two live defects, hard-block outbound sends, gate the prod path #88
2 changed files with 38 additions and 2 deletions
|
|
@ -6,7 +6,7 @@ pages:
|
||||||
about:
|
about:
|
||||||
title: "About Thermograph: how the weather grades are calculated"
|
title: "About Thermograph: how the weather grades are calculated"
|
||||||
description: >-
|
description: >-
|
||||||
How Thermograph works: ~45 years of ERA5 climate history, a ±7-day
|
How Thermograph works: ~45 years of ERA5 climate history, a ±7-day
|
||||||
seasonal window, and empirical percentiles that grade each day relative to
|
seasonal window, and empirical percentiles that grade each day relative to
|
||||||
its own location.
|
its own location.
|
||||||
privacy:
|
privacy:
|
||||||
|
|
@ -21,7 +21,7 @@ pages:
|
||||||
temperatures by month, all-time records, and how today's weather compares
|
temperatures by month, all-time records, and how today's weather compares
|
||||||
to local history.
|
to local history.
|
||||||
glossary_index:
|
glossary_index:
|
||||||
title: "Weather & climate glossary: heat index, feels-like, percentile, and more"
|
title: "Weather & climate glossary: heat index, feels-like, percentile, and more"
|
||||||
description: >-
|
description: >-
|
||||||
Plain-language definitions of weather and climate terms: climate normal,
|
Plain-language definitions of weather and climate terms: climate normal,
|
||||||
percentile, temperature anomaly, feels-like, heat index, wind chill,
|
percentile, temperature anomaly, feels-like, heat index, wind chill,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package contentdata
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -109,3 +110,38 @@ func TestLoadRealContentFiles(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pages.yaml's title/description are interpolated as plain strings into
|
||||||
|
// <title> and <meta name="description"> (base.html.tmpl), so html/template
|
||||||
|
// escapes them. An HTML entity written in the YAML is therefore escaped a
|
||||||
|
// second time and the user sees the literal source: "±7-day" in the
|
||||||
|
// SERP snippet, "Weather & climate glossary" in the browser tab. Both
|
||||||
|
// shipped live until this test existed.
|
||||||
|
//
|
||||||
|
// glossary.yaml's `body` is deliberately NOT checked: it is typed
|
||||||
|
// template.HTML (see glossary_term.html.tmpl) and rendered raw, so entities
|
||||||
|
// and <b> tags there are correct and must stay.
|
||||||
|
func TestRealPagesHaveNoDoubleEscapedEntities(t *testing.T) {
|
||||||
|
dir := filepath.Join("..", "..", "..", "content")
|
||||||
|
if _, err := os.Stat(filepath.Join(dir, "pages.yaml")); err != nil {
|
||||||
|
t.Skipf("real content dir not available: %v", err)
|
||||||
|
}
|
||||||
|
pages, err := LoadPages(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LoadPages(real): %v", err)
|
||||||
|
}
|
||||||
|
// Named (&), decimal (±) and hex (±) entity forms.
|
||||||
|
entity := regexp.MustCompile(`&([a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#[xX][0-9a-fA-F]+);`)
|
||||||
|
for key, p := range pages {
|
||||||
|
for field, value := range map[string]string{
|
||||||
|
"title": p.Title, "description": p.Description,
|
||||||
|
} {
|
||||||
|
if m := entity.FindString(value); m != "" {
|
||||||
|
t.Errorf("pages.yaml[%s].%s contains the HTML entity %q; "+
|
||||||
|
"write the character literally (this field is escaped at "+
|
||||||
|
"render time, so the entity reaches the user as source)",
|
||||||
|
key, field, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue