Skip to content

Commit 1f30946

Browse files
authored
feat: reverts message in json output and allows configuration (#1082)
* feat: allow future deprecated message response with config * tests: middleware * fix: middleware order * fix: frontend using message in body * fix: cors * feat: modify response type with header * fix(webapp): added new headers * dist: updated webapp files * test(e2e): fixes * fix: middleware returning body for 204 requests * fix: frontend apis * tests: cors middleware
1 parent d419818 commit 1f30946

File tree

26 files changed

+912
-659
lines changed

26 files changed

+912
-659
lines changed

e2e/playwright/auth_test.go

Lines changed: 58 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package playwright_test
1+
package playwright
22

33
import (
44
"fmt"
@@ -14,146 +14,110 @@ func TestAuth(t *testing.T) {
1414
container := e2eutil.NewShioriContainer(t, "")
1515
baseURL := fmt.Sprintf("http://localhost:%s", container.GetPort())
1616

17-
// Initialize the browser
18-
pw, err := playwright.Run()
19-
require.NoError(t, err, "Initialize Playwright")
20-
defer pw.Stop()
21-
22-
browser, err := pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
23-
Headless: playwright.Bool(true),
24-
})
25-
require.NoError(t, err, "Launch browser")
26-
defer browser.Close()
17+
mainTestHelper, err := NewTestHelper(t, "main")
18+
require.NoError(t, err)
19+
defer mainTestHelper.Close()
2720

2821
t.Run("successful login with default credentials", func(t *testing.T) {
29-
context, err := browser.NewContext()
30-
require.NoError(t, err, "Create browser context")
31-
32-
t.Cleanup(func() {
33-
context.Close()
34-
})
35-
36-
page, err := context.NewPage()
37-
require.NoError(t, err, "Create new page")
38-
defer page.Close()
39-
4022
// Navigate to the login page
41-
_, err = page.Goto(baseURL)
42-
require.NoError(t, err, "Navigate to login page")
23+
_, err = mainTestHelper.page.Goto(baseURL)
24+
mainTestHelper.Require().NoError(t, err, "Navigate to base URL")
4325

4426
// Get locators for form elements
45-
usernameLocator := page.Locator("#username")
46-
passwordLocator := page.Locator("#password")
47-
buttonLocator := page.Locator(".button")
27+
usernameLocator := mainTestHelper.page.Locator("#username")
28+
passwordLocator := mainTestHelper.page.Locator("#password")
29+
buttonLocator := mainTestHelper.page.Locator(".button")
4830

4931
// Wait for and fill the login form
50-
require.NoError(t, usernameLocator.WaitFor())
51-
require.NoError(t, usernameLocator.Fill("shiori"))
52-
require.NoError(t, passwordLocator.Fill("gopher"))
32+
mainTestHelper.Require().NoError(t, usernameLocator.WaitFor(), "Wait for username field")
33+
mainTestHelper.Require().NoError(t, usernameLocator.Fill("shiori"), "Fill username field")
34+
mainTestHelper.Require().NoError(t, passwordLocator.Fill("gopher"), "Fill password field")
5335

5436
// Click login and wait for success
55-
require.NoError(t, buttonLocator.Click())
56-
require.NoError(t, page.Locator("#bookmarks-grid").WaitFor())
37+
mainTestHelper.Require().NoError(t, buttonLocator.Click(), "Click login button")
38+
mainTestHelper.Require().NoError(t, mainTestHelper.page.Locator("#bookmarks-grid").WaitFor(playwright.LocatorWaitForOptions{
39+
State: playwright.WaitForSelectorStateVisible,
40+
Timeout: playwright.Float(1000),
41+
}), "Wait for bookmarks section to show up")
5742
})
5843

5944
t.Run("failed login with wrong username", func(t *testing.T) {
60-
context, err := browser.NewContext()
45+
th, err := NewTestHelper(t, t.Name())
6146
require.NoError(t, err)
62-
63-
t.Cleanup(func() {
64-
context.Close()
65-
})
66-
67-
page, err := context.NewPage()
68-
require.NoError(t, err)
69-
defer page.Close()
47+
defer th.Close()
7048

7149
// Navigate to the login page
72-
_, err = page.Goto(baseURL)
73-
require.NoError(t, err)
50+
_, err = th.page.Goto(baseURL)
51+
th.Require().NoError(t, err, "Navigate to base URL")
7452

7553
// Get locators for form elements
76-
usernameLocator := page.Locator("#username")
77-
passwordLocator := page.Locator("#password")
78-
buttonLocator := page.Locator(".button")
79-
errorLocator := page.Locator(".error-message")
54+
usernameLocator := th.page.Locator("#username")
55+
passwordLocator := th.page.Locator("#password")
56+
buttonLocator := th.page.Locator(".button")
57+
errorLocator := th.page.Locator(".error-message")
8058

8159
// Wait for and fill the login form
82-
require.NoError(t, usernameLocator.WaitFor())
83-
require.NoError(t, usernameLocator.Fill("wrong_user"))
84-
require.NoError(t, passwordLocator.Fill("gopher"))
60+
th.Require().NoError(t, usernameLocator.WaitFor(), "Wait for username field")
61+
th.Require().NoError(t, usernameLocator.Fill("wrong_user"), "Fill username field")
62+
th.Require().NoError(t, passwordLocator.Fill("gopher"), "Fill password field")
8563

8664
// Click login and verify error
87-
require.NoError(t, buttonLocator.Click())
65+
th.Require().NoError(t, buttonLocator.Click(), "Click login button")
8866
errorText, err := errorLocator.TextContent()
89-
require.NoError(t, err, "Get error message text")
90-
require.Contains(t, errorText, "username or password do not match", "Verify error message for wrong username")
67+
th.Require().NoError(t, err, "Get error message text")
68+
th.Require().Contains(t, errorText, "username or password do not match")
9169
})
9270

9371
t.Run("failed login with wrong password", func(t *testing.T) {
94-
context, err := browser.NewContext()
72+
th, err := NewTestHelper(t, t.Name())
9573
require.NoError(t, err)
96-
97-
t.Cleanup(func() {
98-
context.Close()
99-
})
100-
101-
page, err := context.NewPage()
102-
require.NoError(t, err)
103-
defer page.Close()
74+
defer th.Close()
10475

10576
// Navigate to the login page
106-
_, err = page.Goto(baseURL)
107-
require.NoError(t, err)
77+
_, err = th.page.Goto(baseURL)
78+
th.Require().NoError(t, err, "Navigate to base URL")
10879

10980
// Get locators for form elements
110-
usernameLocator := page.Locator("#username")
111-
passwordLocator := page.Locator("#password")
112-
buttonLocator := page.Locator(".button")
113-
errorLocator := page.Locator(".error-message")
81+
usernameLocator := th.page.Locator("#username")
82+
passwordLocator := th.page.Locator("#password")
83+
buttonLocator := th.page.Locator(".button")
84+
errorLocator := th.page.Locator(".error-message")
11485

11586
// Wait for and fill the login form
116-
require.NoError(t, usernameLocator.WaitFor())
117-
require.NoError(t, usernameLocator.Fill("shiori"))
118-
require.NoError(t, passwordLocator.Fill("wrong_password"))
87+
th.Require().NoError(t, usernameLocator.WaitFor(), "Wait for username field")
88+
th.Require().NoError(t, usernameLocator.Fill("shiori"), "Fill username field")
89+
th.Require().NoError(t, passwordLocator.Fill("wrong_password"), "Fill password field")
11990

12091
// Click login and verify error
121-
require.NoError(t, buttonLocator.Click())
92+
th.Require().NoError(t, buttonLocator.Click(), "Click login button")
12293
errorText, err := errorLocator.TextContent()
123-
require.NoError(t, err)
124-
require.Contains(t, errorText, "username or password do not match")
94+
th.Require().NoError(t, err, "Get error message text")
95+
th.Require().Contains(t, errorText, "username or password do not match")
12596
})
12697

12798
t.Run("empty username validation", func(t *testing.T) {
128-
context, err := browser.NewContext()
99+
th, err := NewTestHelper(t, t.Name())
129100
require.NoError(t, err)
130-
131-
t.Cleanup(func() {
132-
context.Close()
133-
})
134-
135-
page, err := context.NewPage()
136-
require.NoError(t, err)
137-
defer page.Close()
101+
defer th.Close()
138102

139103
// Navigate to the login page
140-
_, err = page.Goto(baseURL)
141-
require.NoError(t, err)
104+
_, err = th.page.Goto(baseURL)
105+
th.Require().NoError(t, err, "Navigate to base URL")
142106

143107
// Get locators for form elements
144-
usernameLocator := page.Locator("#username")
145-
passwordLocator := page.Locator("#password")
146-
buttonLocator := page.Locator(".button")
147-
errorLocator := page.Locator(".error-message")
108+
usernameLocator := th.page.Locator("#username")
109+
passwordLocator := th.page.Locator("#password")
110+
buttonLocator := th.page.Locator(".button")
111+
errorLocator := th.page.Locator(".error-message")
148112

149113
// Wait for form and fill only password
150-
require.NoError(t, usernameLocator.WaitFor())
151-
require.NoError(t, passwordLocator.Fill("gopher"))
114+
th.Require().NoError(t, usernameLocator.WaitFor(), "Wait for username field")
115+
th.Require().NoError(t, passwordLocator.Fill("gopher"), "Fill password field")
152116

153117
// Click login and verify error
154-
require.NoError(t, buttonLocator.Click())
118+
th.Require().NoError(t, buttonLocator.Click(), "Click login button")
155119
errorText, err := errorLocator.TextContent()
156-
require.NoError(t, err)
157-
require.Contains(t, errorText, "Username must not empty")
120+
th.Require().NoError(t, err, "Get error message text")
121+
th.Require().Contains(t, errorText, "Username must not empty")
158122
})
159123
}

e2e/playwright/testhelper.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66
"path"
77
"path/filepath"
8+
"strings"
89
"testing"
910
"time"
1011

@@ -191,6 +192,15 @@ func (pr *PlaywrightRequire) Error(t *testing.T, err error, msgAndArgs ...interf
191192
pr.Assertions.Error(err, msgAndArgs...)
192193
}
193194

195+
func (pr *PlaywrightRequire) Contains(t *testing.T, text, expected string, msgAndArgs ...interface{}) {
196+
pr.Assert(t, func() error {
197+
if !strings.Contains(text, expected) {
198+
return fmt.Errorf("Expected text to contain '%s' but got '%s'", expected, text)
199+
}
200+
return nil
201+
}, msgAndArgs...)
202+
}
203+
194204
// Close cleans up resources and generates the report
195205
func (th *TestHelper) Close() {
196206
if err := GetReporter().GenerateHTML(); err != nil {

internal/http/http.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ func ToHTTPHandler(deps model.Dependencies, h model.HttpHandler, middlewares ...
2525
// Execute handler
2626
h(deps, c)
2727

28-
// Execute OnResponse middlewares
29-
for _, m := range middlewares {
28+
// Execute OnResponse middlewares in reverse order
29+
for i := len(middlewares) - 1; i >= 0; i-- {
30+
m := middlewares[i]
3031
if err := m.OnResponse(deps, c); err != nil {
3132
deps.Logger().WithError(err).Error("middleware error in response")
3233
return

internal/http/middleware/cors.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ type CORSMiddleware struct {
1313
func (m *CORSMiddleware) OnRequest(deps model.Dependencies, c model.WebContext) error {
1414
c.ResponseWriter().Header().Set("Access-Control-Allow-Origin", strings.Join(m.allowedOrigins, ", "))
1515
c.ResponseWriter().Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
16-
c.ResponseWriter().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
16+
c.ResponseWriter().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Shiori-Response-Format")
1717
return nil
1818
}
1919

2020
func (m *CORSMiddleware) OnResponse(deps model.Dependencies, c model.WebContext) error {
2121
c.ResponseWriter().Header().Set("Access-Control-Allow-Origin", strings.Join(m.allowedOrigins, ", "))
2222
c.ResponseWriter().Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
23-
c.ResponseWriter().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
23+
c.ResponseWriter().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Shiori-Response-Format")
2424
return nil
2525
}
2626

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package middleware
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"strings"
7+
"testing"
8+
9+
"github.com/go-shiori/shiori/internal/http/webcontext"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestCORSMiddleware(t *testing.T) {
15+
t.Run("test single origin", func(t *testing.T) {
16+
allowedOrigins := []string{"http://localhost:8080"}
17+
middleware := NewCORSMiddleware(allowedOrigins)
18+
19+
w := httptest.NewRecorder()
20+
r := httptest.NewRequest(http.MethodGet, "/", nil)
21+
c := webcontext.NewWebContext(w, r)
22+
23+
err := middleware.OnRequest(nil, c)
24+
require.NoError(t, err)
25+
26+
headers := w.Header()
27+
assert.Equal(t, "http://localhost:8080", headers.Get("Access-Control-Allow-Origin"))
28+
assert.Equal(t, "GET, POST, PUT, DELETE, OPTIONS", headers.Get("Access-Control-Allow-Methods"))
29+
assert.Equal(t, "Content-Type, Authorization, X-Shiori-Response-Format", headers.Get("Access-Control-Allow-Headers"))
30+
})
31+
32+
t.Run("test multiple origins", func(t *testing.T) {
33+
allowedOrigins := []string{"http://localhost:8080", "http://example.com"}
34+
middleware := NewCORSMiddleware(allowedOrigins)
35+
36+
w := httptest.NewRecorder()
37+
r := httptest.NewRequest(http.MethodGet, "/", nil)
38+
c := webcontext.NewWebContext(w, r)
39+
40+
err := middleware.OnRequest(nil, c)
41+
require.NoError(t, err)
42+
43+
headers := w.Header()
44+
assert.Equal(t, strings.Join(allowedOrigins, ", "), headers.Get("Access-Control-Allow-Origin"))
45+
})
46+
47+
t.Run("test empty origins", func(t *testing.T) {
48+
middleware := NewCORSMiddleware([]string{})
49+
50+
w := httptest.NewRecorder()
51+
r := httptest.NewRequest(http.MethodGet, "/", nil)
52+
c := webcontext.NewWebContext(w, r)
53+
54+
err := middleware.OnRequest(nil, c)
55+
require.NoError(t, err)
56+
57+
headers := w.Header()
58+
assert.Equal(t, "", headers.Get("Access-Control-Allow-Origin"))
59+
})
60+
61+
t.Run("test OnResponse headers", func(t *testing.T) {
62+
allowedOrigins := []string{"http://localhost:8080"}
63+
middleware := NewCORSMiddleware(allowedOrigins)
64+
65+
w := httptest.NewRecorder()
66+
r := httptest.NewRequest(http.MethodGet, "/", nil)
67+
c := webcontext.NewWebContext(w, r)
68+
69+
err := middleware.OnResponse(nil, c)
70+
require.NoError(t, err)
71+
72+
headers := w.Header()
73+
assert.Equal(t, "http://localhost:8080", headers.Get("Access-Control-Allow-Origin"))
74+
assert.Equal(t, "GET, POST, PUT, DELETE, OPTIONS", headers.Get("Access-Control-Allow-Methods"))
75+
assert.Equal(t, "Content-Type, Authorization, X-Shiori-Response-Format", headers.Get("Access-Control-Allow-Headers"))
76+
})
77+
}

0 commit comments

Comments
 (0)