183 lines
5.6 KiB
Go
183 lines
5.6 KiB
Go
package gonexapp_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/chromedp/cdproto/network"
|
|
"github.com/chromedp/chromedp"
|
|
|
|
"gitea.neitzel.de/konrad/go-nc-exapp"
|
|
)
|
|
|
|
const browserLanguage = "de-DE,de;q=0.9"
|
|
|
|
func TestUILanguageChrome(t *testing.T) {
|
|
german := chromeSeen{
|
|
lang: "de", open: "Navigation öffnen", close: "Navigation schließen",
|
|
fold: "Domains einklappen", cancel: "Abbrechen", ok: "OK",
|
|
}
|
|
english := chromeSeen{
|
|
lang: "en", open: "Open navigation", close: "Close navigation",
|
|
fold: "Fold Domains", cancel: "Cancel", ok: "OK",
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
user bool
|
|
status int
|
|
ncLang string
|
|
want chromeSeen
|
|
}{
|
|
{name: "german", user: true, status: http.StatusOK, ncLang: "de", want: german},
|
|
{name: "english", user: true, status: http.StatusOK, ncLang: "en", want: english},
|
|
{name: "formal german", user: true, status: http.StatusOK, ncLang: "de_DE", want: german},
|
|
{name: "british english", user: true, status: http.StatusOK, ncLang: "en_GB", want: english},
|
|
{name: "french", user: true, status: http.StatusOK, ncLang: "fr", want: english},
|
|
{name: "failed read", user: true, status: http.StatusInternalServerError, ncLang: "de", want: english},
|
|
{name: "missing user", user: false, status: http.StatusOK, ncLang: "de", want: english},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
nc := fakeLanguage(tt.status, tt.ncLang)
|
|
t.Cleanup(nc.Close)
|
|
page := languagePage(nc.URL)
|
|
t.Cleanup(page.Close)
|
|
|
|
headers := network.Headers{"Accept-Language": browserLanguage}
|
|
if tt.user {
|
|
headers["AUTHORIZATION-APP-API"] = base64.StdEncoding.EncodeToString([]byte("alice:secret"))
|
|
}
|
|
got := readChrome(t, page.URL+"/?item=domains", headers)
|
|
if !strings.Contains(got.page, "domains-page") {
|
|
t.Fatalf("page did not render: %+v", got)
|
|
}
|
|
if got.lang != tt.want.lang || got.open != tt.want.open || got.close != tt.want.close ||
|
|
got.fold != tt.want.fold || got.cancel != tt.want.cancel || got.ok != tt.want.ok || got.agree != "Remove" {
|
|
t.Fatalf("got %+v, want %+v with agree %q", got, tt.want, "Remove")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type chromeSeen struct {
|
|
lang string
|
|
open string
|
|
close string
|
|
fold string
|
|
cancel string
|
|
ok string
|
|
agree string
|
|
page string
|
|
}
|
|
|
|
func fakeLanguage(status int, language string) *httptest.Server {
|
|
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet || !strings.Contains(r.URL.Path, "/cloud/users/") {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
if status != http.StatusOK {
|
|
http.Error(w, "language unavailable", status)
|
|
return
|
|
}
|
|
// A read that drops the browser language must not look like a hit.
|
|
lang := language
|
|
if r.Header.Get("Accept-Language") != browserLanguage {
|
|
lang = "en"
|
|
}
|
|
// locale differs from language so a read of the wrong field fails the page.
|
|
locale := "fr"
|
|
if language == "fr" || language == "en_GB" {
|
|
locale = "de"
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
fmt.Fprintf(w, `{"ocs":{"meta":{"status":"ok","statuscode":200,"message":"OK"},"data":{"language":%q,"locale":%q}}}`, lang, locale)
|
|
}))
|
|
}
|
|
|
|
func languagePage(ncBase string) *httptest.Server {
|
|
nav := sampleNavigation()
|
|
nav.Cred = gonexapp.Credentials{
|
|
BaseURL: ncBase,
|
|
AppID: "exapp",
|
|
AppVersion: "1.0.0",
|
|
AAVersion: "1.0.0",
|
|
AppSecret: "secret",
|
|
}
|
|
nav.Page = func(_ *http.Request, id string) (string, bool) {
|
|
switch id {
|
|
case "domains":
|
|
return `<p>domains-page</p><button type="button" id="ask">Ask</button>
|
|
<script>
|
|
document.getElementById("ask").addEventListener("click", function () {
|
|
exappDialog.confirm({ text: "Sure?", agree: "Remove" });
|
|
});
|
|
</script>`, true
|
|
case "keys":
|
|
return "<p>keys-page</p>", true
|
|
case "example.com":
|
|
return "<p>zone-page</p>", true
|
|
default:
|
|
return "", false
|
|
}
|
|
}
|
|
return httptest.NewServer(nav.Handler())
|
|
}
|
|
|
|
func readChrome(t *testing.T, pageURL string, headers network.Headers) chromeSeen {
|
|
t.Helper()
|
|
ctx, cancel := chromedp.NewContext(allocCtx)
|
|
t.Cleanup(cancel)
|
|
ctx, cancel = context.WithTimeout(ctx, 25*time.Second)
|
|
t.Cleanup(cancel)
|
|
|
|
var raw, closeLabel, agree string
|
|
err := chromedp.Run(ctx,
|
|
network.Enable(),
|
|
network.SetExtraHTTPHeaders(headers),
|
|
chromedp.Navigate(pageURL),
|
|
chromedp.Evaluate(chromeProbeJS, &raw),
|
|
chromedp.Evaluate(`document.getElementById("nav-toggle").click()`, nil),
|
|
chromedp.Evaluate(`document.getElementById("nav-toggle").getAttribute("aria-label")`, &closeLabel),
|
|
chromedp.Evaluate(`document.getElementById("ask").click()`, nil),
|
|
chromedp.Evaluate(`document.getElementById("exapp-dialog-agree").textContent`, &agree),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var probe struct {
|
|
Lang string `json:"lang"`
|
|
Open string `json:"open"`
|
|
Fold string `json:"fold"`
|
|
Cancel string `json:"cancel"`
|
|
OK string `json:"ok"`
|
|
Page string `json:"page"`
|
|
}
|
|
if err := json.Unmarshal([]byte(raw), &probe); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return chromeSeen{
|
|
lang: probe.Lang, open: probe.Open, close: closeLabel, fold: probe.Fold,
|
|
cancel: probe.Cancel, ok: probe.OK, agree: agree, page: probe.Page,
|
|
}
|
|
}
|
|
|
|
const chromeProbeJS = `(() => {
|
|
const fold = document.querySelector("button.fold");
|
|
return JSON.stringify({
|
|
lang: document.documentElement.lang,
|
|
open: document.getElementById("nav-toggle").getAttribute("aria-label"),
|
|
fold: fold ? fold.getAttribute("aria-label") : "",
|
|
cancel: document.getElementById("exapp-dialog-cancel").textContent,
|
|
ok: document.getElementById("exapp-dialog-agree").textContent,
|
|
page: document.querySelector("main").textContent
|
|
});
|
|
})()`
|