веб-ui: знак «jb» стал единым для шапки, иконки вкладки и превью ссылки
- логотип сведён в web/static/logo.svg (буквы геометрией, оптическая центровка); прежний favicon с «play» убран - <head> шести страниц свёрнут в партиал head с og-мета; адреса абсолютные, база берётся из запроса, хост из недоверенного заголовка проверяется - растровые ассеты og-cover.jpg и apple-touch-icon.png собирает `task brand` из исходников web/*.html
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// getWith — GET с заголовками (за обратным прокси мету считает не Host).
|
||||
func getWith(t *testing.T, h http.Handler, path string, hdr map[string]string) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
req := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
for k, v := range hdr {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
rr := httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
return rr
|
||||
}
|
||||
|
||||
// TestPageMetaAbsolute — превью ссылки: адреса картинки и страницы абсолютные,
|
||||
// иначе краулер мессенджера картинку не заберёт.
|
||||
func TestPageMetaAbsolute(t *testing.T) {
|
||||
h := testRouter(t, stubReader{}, stubReviewer{})
|
||||
|
||||
body := get(t, h, "/").Body.String()
|
||||
if !strings.Contains(body, `<meta property="og:image" content="http://example.com/static/og-cover.jpg?v=`) {
|
||||
t.Errorf("og:image не абсолютный: %s", metaLine(body, "og:image"))
|
||||
}
|
||||
if !strings.Contains(body, `<meta property="og:url" content="http://example.com/">`) {
|
||||
t.Errorf("og:url не абсолютный: %s", metaLine(body, "og:url"))
|
||||
}
|
||||
if !strings.Contains(body, `<meta property="og:title" content="jellybit — загрузки">`) {
|
||||
t.Errorf("og:title не заголовок страницы: %s", metaLine(body, "og:title"))
|
||||
}
|
||||
if !strings.Contains(body, `<title>jellybit — загрузки</title>`) {
|
||||
t.Error("заголовок вкладки потерян")
|
||||
}
|
||||
}
|
||||
|
||||
// TestPageMetaBehindProxy — за прокси схему и хост несут X-Forwarded-*;
|
||||
// из списка значений берётся первое (ближнее к клиенту).
|
||||
func TestPageMetaBehindProxy(t *testing.T) {
|
||||
h := testRouter(t, stubReader{}, stubReviewer{})
|
||||
|
||||
body := getWith(t, h, "/", map[string]string{
|
||||
"X-Forwarded-Proto": "https, http",
|
||||
"X-Forwarded-Host": "jellybit.example.org, internal",
|
||||
}).Body.String()
|
||||
|
||||
if !strings.Contains(body, `content="https://jellybit.example.org/static/og-cover.jpg?v=`) {
|
||||
t.Errorf("og:image мимо прокси: %s", metaLine(body, "og:image"))
|
||||
}
|
||||
if !strings.Contains(body, `<meta property="og:url" content="https://jellybit.example.org/">`) {
|
||||
t.Errorf("og:url мимо прокси: %s", metaLine(body, "og:url"))
|
||||
}
|
||||
}
|
||||
|
||||
// TestPageMetaBadHost — хост непохожей формы отбрасывается целиком: в мете
|
||||
// остаётся относительный адрес, чужой хост в превью не подставляется.
|
||||
func TestPageMetaBadHost(t *testing.T) {
|
||||
h := testRouter(t, stubReader{}, stubReviewer{})
|
||||
|
||||
body := getWith(t, h, "/", map[string]string{
|
||||
"X-Forwarded-Host": `evil.example.org"><script>`,
|
||||
}).Body.String()
|
||||
|
||||
if !strings.Contains(body, `<meta property="og:image" content="/static/og-cover.jpg?v=`) {
|
||||
t.Errorf("порченый хост попал в мету: %s", metaLine(body, "og:image"))
|
||||
}
|
||||
if strings.Contains(body, "<script>") {
|
||||
t.Error("заголовок запроса дотёк в разметку неэкранированным")
|
||||
}
|
||||
}
|
||||
|
||||
// TestBrandAssets — знак один на все места: иконка вкладки и шапка ссылаются на
|
||||
// тот же /static/logo.svg, и он отдаётся.
|
||||
func TestBrandAssets(t *testing.T) {
|
||||
h := testRouter(t, stubReader{}, stubReviewer{})
|
||||
|
||||
body := get(t, h, "/").Body.String()
|
||||
if strings.Count(body, "/static/logo.svg") != 2 {
|
||||
t.Errorf("ожидались две ссылки на знак (icon + шапка), получено %d",
|
||||
strings.Count(body, "/static/logo.svg"))
|
||||
}
|
||||
if strings.Contains(body, "favicon.svg") {
|
||||
t.Error("осталась ссылка на прежний favicon.svg")
|
||||
}
|
||||
|
||||
for _, p := range []string{"/static/logo.svg", "/static/og-cover.jpg", "/static/apple-touch-icon.png"} {
|
||||
if code := get(t, h, p).Code; code != http.StatusOK {
|
||||
t.Errorf("GET %s = %d, want 200", p, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// metaLine — строка ответа с искомым мета-тегом (для внятного отчёта теста).
|
||||
func metaLine(body, prop string) string {
|
||||
for _, l := range strings.Split(body, "\n") {
|
||||
if strings.Contains(l, prop) {
|
||||
return strings.TrimSpace(l)
|
||||
}
|
||||
}
|
||||
return "(тега нет)"
|
||||
}
|
||||
Reference in New Issue
Block a user