tests for restricted UI behavior

* add tests for privacy mode settings
* test privacy mode settings for member role
* test default language settings
* test denied keys interface for each role
* test adding new member interface depending on role
* test member details depending on role
* test invite button is disabled pending on user role
This commit is contained in:
cblgh 2021-04-22 10:36:59 +02:00 committed by Henry
parent 2c9fdcb98e
commit cec5f93fb6
10 changed files with 322 additions and 17 deletions

View File

@ -8,6 +8,7 @@ import (
"net/url"
"testing"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
@ -32,6 +33,85 @@ func TestDeniedKeysEmpty(t *testing.T) {
})
}
func TestDeniedKeysDisabledInterface(t *testing.T) {
ts := newSession(t)
a := assert.New(t)
listURL := ts.URLTo(router.AdminDeniedKeysOverview)
ts.User = roomdb.Member{
ID: 1234,
Role: roomdb.RoleAdmin,
}
html, resp := ts.Client.GetHTML(listURL)
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
formSelection := html.Find("form#add-entry")
a.EqualValues(1, formSelection.Length())
method, ok := formSelection.Attr("method")
a.True(ok, "form has method set")
a.Equal("POST", method)
action, ok := formSelection.Attr("action")
a.True(ok, "form has action set")
addURL := ts.URLTo(router.AdminDeniedKeysAdd)
a.Equal(addURL.String(), action)
webassert.ElementsInForm(t, formSelection, []webassert.FormElement{
{Name: "pub_key", Type: "text"},
{Name: "comment", Type: "text"},
})
newKey := "@x7iOLUcq3o+sjGeAnipvWeGzfuYgrXl8L4LYlxIhwDc=.ed25519"
addVals := url.Values{
"comment": []string{"some comment"},
// just any key that looks valid
"pub_key": []string{newKey},
}
rec := ts.Client.PostForm(addURL, addVals)
a.Equal(http.StatusTemporaryRedirect, rec.Code)
a.Equal(1, ts.DeniedKeysDB.AddCallCount())
_, addedKey, addedComment := ts.DeniedKeysDB.AddArgsForCall(0)
a.Equal(newKey, addedKey.Ref())
a.Equal("some comment", addedComment)
/* Verify that the inputs are visible/hidden depending on user roles */
checkInputsAreDisabled := func(shouldBeDisabled bool) {
html, resp = ts.Client.GetHTML(listURL)
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
inputContainer := html.Find("#denied-keys-input-container")
a.Equal(1, inputContainer.Length())
inputs := inputContainer.Find("input")
// pubkey, comment, submit button
a.Equal(3, inputs.Length())
inputs.Each(func(i int, el *goquery.Selection) {
_, disabled := el.Attr("disabled")
a.Equal(shouldBeDisabled, disabled)
})
}
// verify that inputs are enabled for RoleAdmin
checkInputsAreDisabled(false)
// verify that inputs are enabled for RoleModerator
ts.User = roomdb.Member{
ID: 9001,
Role: roomdb.RoleModerator,
}
checkInputsAreDisabled(false)
// verify that inputs are disabled for RoleMember
ts.User = roomdb.Member{
ID: 7331,
Role: roomdb.RoleMember,
}
checkInputsAreDisabled(true)
}
func TestDeniedKeysAdd(t *testing.T) {
ts := newSession(t)
a := assert.New(t)

View File

@ -64,6 +64,50 @@ func TestInvitesOverview(t *testing.T) {
a.True(yes, "a-tag has href attribute")
wantURL := ts.URLTo(router.AdminInvitesRevokeConfirm, "id", 666)
a.Equal(wantURL.String(), link)
testInviteButtonDisabled := func(shouldBeDisabled bool) {
html, resp = ts.Client.GetHTML(invitesOverviewURL)
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
inviteButton := html.Find("#create-invite button")
_, disabled := inviteButton.Attr("disabled")
a.EqualValues(shouldBeDisabled, disabled, "invite button should be disabled")
}
// member, mod, admin should all be able to invite in ModeCommunity
ts.ConfigDB.GetPrivacyModeReturns(roomdb.ModeCommunity, nil)
ts.User = roomdb.Member{
ID: 1234,
Role: roomdb.RoleAdmin,
}
testInviteButtonDisabled(false)
ts.User = roomdb.Member{
ID: 7331,
Role: roomdb.RoleModerator,
}
testInviteButtonDisabled(false)
ts.User = roomdb.Member{
ID: 9001,
Role: roomdb.RoleMember,
}
testInviteButtonDisabled(false)
// mod and admin should be able to invite, member should not
ts.ConfigDB.GetPrivacyModeReturns(roomdb.ModeRestricted, nil)
ts.User = roomdb.Member{
ID: 1234,
Role: roomdb.RoleAdmin,
}
testInviteButtonDisabled(false)
ts.User = roomdb.Member{
ID: 7331,
Role: roomdb.RoleModerator,
}
testInviteButtonDisabled(false)
ts.User = roomdb.Member{
ID: 9001,
Role: roomdb.RoleMember,
}
testInviteButtonDisabled(true)
}
func TestInvitesCreateForm(t *testing.T) {

View File

@ -7,6 +7,7 @@ import (
"net/url"
"testing"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
@ -35,6 +36,11 @@ func TestMembersAdd(t *testing.T) {
ts := newSession(t)
a := assert.New(t)
ts.User = roomdb.Member{
ID: 1234,
Role: roomdb.RoleAdmin,
}
listURL := ts.URLTo(router.AdminMembersOverview)
html, resp := ts.Client.GetHTML(listURL)
@ -70,6 +76,43 @@ func TestMembersAdd(t *testing.T) {
a.Equal(newKey, addedPubKey.Ref())
a.Equal(roomdb.RoleMember, addedRole)
/* Verify that the inputs are visible/hidden depending on user roles */
checkInputsAreDisabled := func(shouldBeDisabled bool) {
html, resp = ts.Client.GetHTML(listURL)
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
inputContainer := html.Find("#add-member-input-container")
a.Equal(1, inputContainer.Length())
inputs := inputContainer.Find("input")
// pubkey
a.Equal(1, inputs.Length())
inputs.Each(func(i int, el *goquery.Selection) {
_, disabled := el.Attr("disabled")
a.Equal(shouldBeDisabled, disabled)
})
button := inputContainer.Find("button")
a.Equal(1, button.Length())
button.Each(func(i int, el *goquery.Selection) {
_, disabled := el.Attr("disabled")
a.Equal(shouldBeDisabled, disabled)
})
}
// verify that inputs are enabled for RoleAdmin
checkInputsAreDisabled(false)
// verify that inputs are enabled for RoleModerator
ts.User = roomdb.Member{
ID: 9001,
Role: roomdb.RoleModerator,
}
checkInputsAreDisabled(false)
// verify that inputs are disabled for RoleMember
ts.User = roomdb.Member{
ID: 7331,
Role: roomdb.RoleMember,
}
checkInputsAreDisabled(true)
}
func TestMembersDontAddInvalid(t *testing.T) {
@ -173,6 +216,11 @@ func TestMemberDetails(t *testing.T) {
memberURL := ts.URLTo(router.AdminMemberDetails, "id", "1")
ts.User = roomdb.Member{
ID: 1234,
Role: roomdb.RoleAdmin,
}
html, resp := ts.Client.GetHTML(memberURL)
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
@ -180,14 +228,6 @@ func TestMemberDetails(t *testing.T) {
{"title", "AdminMemberDetailsTitle"},
})
// check for SSB ID
ssbID := html.Find("#ssb-id")
a.Equal(feedRef.Ref(), ssbID.Text())
// check for change-role dropdown
roleDropdown := html.Find("#change-role")
a.EqualValues(roleDropdown.Length(), 1)
aliasList := html.Find("#alias-list").Find("a")
// check for link to resolve 1st Alias
@ -220,6 +260,37 @@ func TestMemberDetails(t *testing.T) {
a.True(yes, "a-tag has href attribute")
wantLink = ts.URLTo(router.AdminMembersRemoveConfirm, "id", 1)
a.Equal(wantLink.String(), removeLink)
testDisabledBehaviour := func(isElevated bool) {
html, resp := ts.Client.GetHTML(memberURL)
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
// check for SSB ID
ssbID := html.Find("#ssb-id")
a.Equal(feedRef.Ref(), ssbID.Text())
// check for change-role dropdown
roleDropdown := html.Find("#change-role")
if isElevated {
a.Equal(1, roleDropdown.Length())
} else {
a.Equal(0, roleDropdown.Length())
}
}
testDisabledBehaviour(true)
/* Now: verify that moderators cannot make room settings changes */
ts.User = roomdb.Member{
ID: 7331,
Role: roomdb.RoleModerator,
}
testDisabledBehaviour(true)
/* Finally: verify that members cannot make room settings changes */
ts.User = roomdb.Member{
ID: 9001,
Role: roomdb.RoleMember,
}
testDisabledBehaviour(false)
}
func TestMembersRemoveConfirmation(t *testing.T) {

View File

@ -5,6 +5,7 @@ import (
"strings"
"testing"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
"github.com/stretchr/testify/assert"
)
@ -33,6 +34,10 @@ func TestLanguageSetDefaultLanguage(t *testing.T) {
a := assert.New(t)
ts.ConfigDB.GetDefaultLanguageReturns("de", nil)
ts.User = roomdb.Member{
ID: 1234,
Role: roomdb.RoleAdmin,
}
u := ts.URLTo(router.AdminSettings)
html, resp := ts.Client.GetHTML(u)

View File

@ -0,0 +1,100 @@
package admin
import (
"net/http"
"strings"
"testing"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
)
func TestSettingsOverview(t *testing.T) {
ts := newSession(t)
a := assert.New(t)
/* First: make sure everything renders correctly for admins */
ts.User = roomdb.Member{
ID: 1234,
Role: roomdb.RoleAdmin,
}
ts.ConfigDB.GetPrivacyModeReturns(roomdb.ModeCommunity, nil)
ts.ConfigDB.GetDefaultLanguageReturns("en", nil)
settingsURL := ts.URLTo(router.AdminSettings)
html, resp := ts.Client.GetHTML(settingsURL)
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
// the privacy mode form & its summary/details container should exist
privacyFormContainer := html.Find("#change-privacy")
a.Equal(1, privacyFormContainer.Length())
a.Equal(1, privacyFormContainer.Find("summary").Length())
// chosen privacy mode is ModeCommunity (english translation will only be the name of the label, due to testing suite is set up atm)
a.Equal("ModeCommunity", strings.TrimSpace(privacyFormContainer.Find("summary").Text()))
// details-dropdown should have two forms, one for each of the other two privacy modes
// that can be selected (ModeOpen, ModeRestricted)
a.Equal(2, privacyFormContainer.Find("form").Length())
// and one span, showing the selected mode
a.Equal(1, privacyFormContainer.Find("#selected-mode").Length())
inputs := privacyFormContainer.Find("input")
// verify none of the privacy mode container's inputs are disabled
inputs.Each(func(i int, el *goquery.Selection) {
_, exists := el.Attr("disabled")
a.False(exists)
})
// verify that the change language form exists & is enabled
languageFormContainer := html.Find("#change-language-container")
a.Equal(1, languageFormContainer.Length())
a.Equal(1, languageFormContainer.Find("summary").Length())
// (english translation will only be the name of the label, due to testing suite is set up atm)
a.Equal("LanguageName", strings.TrimSpace(languageFormContainer.Find("summary").Text()))
testDisabledBehaviour := func() {
settingsURL := ts.URLTo(router.AdminSettings)
html, resp := ts.Client.GetHTML(settingsURL)
a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code")
// we do not have the summary/details hack if the forms are hidden
privacyFormContainer := html.Find("#change-privacy")
a.Equal(0, privacyFormContainer.Length())
// the should still be the parent container, however
privacyContainer := html.Find("#privacy-mode-container")
a.Equal(1, privacyContainer.Length())
// there should only be one input in the privacy mode container now
inputs := privacyContainer.Find("input")
a.Equal(1, inputs.Length())
// the input should be disabled
_, disabled := inputs.Attr("disabled")
a.True(disabled)
// next, verify that the change language setting is disabled
languageContainer := html.Find("#change-language-container")
a.Equal(1, languageContainer.Length())
// there should only be one input in the language mode container now
inputs = languageContainer.Find("input")
a.Equal(1, inputs.Length())
// the input should be disabled
_, disabled = inputs.Attr("disabled")
a.True(disabled)
}
/* Now: verify that moderators cannot make room settings changes */
ts.User = roomdb.Member{
ID: 7331,
Role: roomdb.RoleModerator,
}
testDisabledBehaviour()
/* Finally: verify that members cannot make room settings changes */
ts.User = roomdb.Member{
ID: 9001,
Role: roomdb.RoleMember,
}
testDisabledBehaviour()
}

View File

@ -133,9 +133,14 @@ func newSession(t *testing.T) *testSession {
testFuncs["urlToNotice"] = func(name string) string { return "" }
testFuncs["language_count"] = func() int { return 1 }
testFuncs["list_languages"] = func(*url.URL, string) string { return "" }
testFuncs["member_is_elevated"] = func() bool { return true }
testFuncs["member_is_admin"] = func() bool { return true }
testFuncs["member_can_invite"] = func() bool { return true }
testFuncs["member_is_elevated"] = func() bool { return ts.User.Role == roomdb.RoleAdmin || ts.User.Role == roomdb.RoleModerator }
testFuncs["member_is_admin"] = func() bool { return ts.User.Role == roomdb.RoleAdmin }
testFuncs["member_can_invite"] = func() bool {
pm, _ := ts.ConfigDB.GetPrivacyMode(ctx)
memberElevated := ts.User.Role == roomdb.RoleAdmin || ts.User.Role == roomdb.RoleModerator
memberCanInvite := ts.User.Role == roomdb.RoleMember && (pm == roomdb.ModeCommunity || pm == roomdb.ModeOpen)
return memberElevated || memberCanInvite
}
testFuncs["list_languages"] = func(*url.URL, string) string { return "" }
testFuncs["relative_time"] = func(when time.Time) string { return humanize.Time(when) }

View File

@ -100,7 +100,7 @@ func ContextInjecter(mdb roomdb.MembersService, withPassword *auth.Handler, with
//
// {{ member_is_admin }} is a shortcut for {{ member_has_role "RoleAdmin" }}
//
// {{ member_is_admin }} is a shortcut for {{ or member_has_role "RoleAdmin" member_has_role "RoleModerator"}}
// {{ member_is_elevated }} is a shortcut for {{ or member_has_role "RoleAdmin" member_has_role "RoleModerator"}}
func TemplateHelpers() []render.Option {
return []render.Option{

View File

@ -20,7 +20,7 @@
method="POST"
>
{{ .csrfField }}
<div class="flex flex-row items-center h-12">
<div id="denied-keys-input-container" class="flex flex-row items-center h-12">
<input
{{ if member_is_elevated }} {{ else }} disabled {{ end }}
type="text"

View File

@ -15,7 +15,7 @@
>
{{ .csrfField }}
<label class="block mt-6 mb-1 text-sm text-gray-500">{{ i18n "AdminAddNewMemberTitle" }}</label>
<div class="flex flex-row items-center justify-start mb-6">
<div id="add-member-input-container" class="flex flex-row items-center justify-start mb-6">
<input
{{ if member_is_elevated }} {{ else }} disabled {{ end }}
type="text"

View File

@ -4,7 +4,7 @@
class="text-3xl tracking-tight font-black text-black mt-2 mb-0"
>{{ i18n "Settings" }}</h1>
<div class="max-w-2xl">
<div class="max-w-2xl" id="privacy-mode-container">
<h2 class="text-xl tracking-tight font-bold text-black mt-2 mb-2">{{ i18n "PrivacyModesTitle" }}</h2>
<p class="mb-4">
{{ i18n "ExplanationPrivacyModes" }}
@ -39,7 +39,7 @@
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
</svg>
</div>
<span>{{ i18n .String }}</span>
<span id="selected-mode">{{ i18n .String }}</span>
</div>
{{end}}
{{end}}
@ -59,7 +59,7 @@
<div class="text-md col-span-2 italic">{{ i18n "ExplanationRestricted" }}</div>
</div>
</div>
<div class="max-w-2xl">
<div class="max-w-2xl" id="change-language-container">
<h2 class="text-xl tracking-tight font-bold text-black mt-2 mb-2">{{ i18n "DefaultLanguageTitle" }}</h2>
<p class="mb-4">
{{ i18n "ExplanationDefaultLanguage" }}