Skip to content

Commit 4a2b928

Browse files
authored
Merge pull request #37 from pgulb/14-add-tests-for-pwa
14 add tests for pwa
2 parents a7db229 + 6bf0846 commit 4a2b928

File tree

14 files changed

+764
-491
lines changed

14 files changed

+764
-491
lines changed

.github/workflows/docker-publish-pwa.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ name: Build PWA image
22

33
on:
44
push:
5-
branches: [ "main" ]
6-
tags:
7-
- 'v*'
85

96
env:
107
REGISTRY: ghcr.io

.github/workflows/pwa-tests.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Run PWA tests
2+
3+
on:
4+
workflow_run:
5+
workflows:
6+
- Build PWA image
7+
types:
8+
- completed
9+
10+
jobs:
11+
pwa-test:
12+
runs-on: ubuntu-latest
13+
container:
14+
image: ubuntu:latest
15+
services:
16+
pwa:
17+
image: ghcr.io/pgulb/flush-log:pwa
18+
ports:
19+
- 8080:8080
20+
env:
21+
GOAPP_PORT: ':8080'
22+
api:
23+
image: ghcr.io/pgulb/flush-log:api
24+
ports:
25+
- 6789:6789
26+
env:
27+
MONGO_URL: 'mock'
28+
steps:
29+
- name: Checkout
30+
uses: actions/checkout@v4
31+
- name: Setup Go environment
32+
uses: actions/setup-go@v5.0.2
33+
with:
34+
go-version: '1.23.2'
35+
- name: Install go-task
36+
run: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
37+
- name: run unit tests
38+
run: task test-pwa-unit

pwa/.air.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ tmp_dir = "tmp"
99
# Array of commands to run before each build
1010
# pre_cmd = ["echo 'hello air' > pre_cmd.txt"]
1111
# Just plain old shell command. You could use `make` as well.
12-
cmd = "GOARCH=wasm GOOS=js go build -o web/app.wasm && GOARCH=amd64 GOOS=linux go build"
12+
cmd = "GOARCH=wasm GOOS=js go build -o web/app.wasm main/main.go && GOARCH=amd64 GOOS=linux go build -o flush-log main/main.go"
1313
# Array of commands to run after ^C
1414
# post_cmd = ["echo 'hello air' > post_cmd.txt"]
1515
# Binary file yields from `cmd`.
@@ -23,11 +23,11 @@ bin = "flush-log"
2323
# include_ext = ["go"]
2424
# Ignore these filename extensions or directories.
2525
# exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
26-
exclude_dir = ["web"]
26+
exclude_dir = ["web", "tmp", "flush_test"]
2727
# Watch these directories if you specified.
28-
include_dir = []
28+
include_dir = ["flush", "main"]
2929
# Watch these files.
30-
include_file = ["main.go"]
30+
include_file = []
3131
# Exclude files.
3232
exclude_file = []
3333
# Exclude specific regular expressions.

pwa/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ FROM golang:1.23.2-alpine3.20 AS build
22
WORKDIR /app
33
ADD go.mod go.sum ./
44
RUN go mod download
5-
ADD main.go ./
6-
RUN GOARCH=amd64 GOOS=linux go build && GOARCH=wasm GOOS=js go build -o web/app.wasm
5+
ADD main/* ./main/
6+
ADD flush/* ./flush/
7+
RUN GOARCH=wasm GOOS=js go build -o web/app.wasm ./main/main.go && \
8+
GOARCH=amd64 GOOS=linux go build -o flush-log ./main/main.go
79

810
FROM scratch
911
WORKDIR /app

pwa/flush/apicalls.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package flush
2+
3+
import (
4+
"bytes"
5+
"encoding/base64"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
10+
"github.com/maxence-charriere/go-app/v10/pkg/app"
11+
)
12+
13+
func CloseBody(r *http.Response) {
14+
if err := r.Body.Close(); err != nil {
15+
DisplayError(err)
16+
}
17+
}
18+
19+
func GetApiUrl() (string, error) {
20+
r, err := http.Get("web/apiurl")
21+
if err != nil {
22+
return "", err
23+
}
24+
defer CloseBody(r)
25+
bytes, err := io.ReadAll(r.Body)
26+
if err != nil {
27+
return "", err
28+
}
29+
return string(bytes), nil
30+
}
31+
32+
func TryLogin(username string, password string) (int, string, error) {
33+
apiUrl, err := GetApiUrl()
34+
if err != nil {
35+
return 0, "", err
36+
}
37+
basicAuth := BasicAuth(username, password)
38+
req, err := AuthorizedRequest("GET", apiUrl, basicAuth)
39+
if err != nil {
40+
return 0, "", err
41+
}
42+
r, err := http.DefaultClient.Do(req)
43+
if err != nil {
44+
return 0, "", err
45+
}
46+
defer CloseBody(r)
47+
return r.StatusCode, basicAuth, nil
48+
}
49+
50+
func TryRegister(username string, password string) (int, string, error) {
51+
apiUrl, err := GetApiUrl()
52+
if err != nil {
53+
return 0, "", err
54+
}
55+
js := []byte(fmt.Sprintf(`
56+
{
57+
"username": "%s",
58+
"password": "%s"
59+
}
60+
`, username, password))
61+
req, err := http.NewRequest("POST", apiUrl+"/user", bytes.NewBuffer(js))
62+
if err != nil {
63+
return 0, "", err
64+
}
65+
basicAuth := BasicAuth(username, password)
66+
r, err := http.DefaultClient.Do(req)
67+
if err != nil {
68+
return 0, "", err
69+
}
70+
defer CloseBody(r)
71+
return r.StatusCode, basicAuth, nil
72+
}
73+
74+
func GetFlushesFromApi(ctx app.Context) (string, error) {
75+
apiUrl, err := GetApiUrl()
76+
if err != nil {
77+
return "", err
78+
}
79+
req, err := http.NewRequest("GET", apiUrl, nil)
80+
if err != nil {
81+
return "", err
82+
}
83+
var c Creds
84+
ctx.GetState("creds", &c)
85+
req.Header.Add("Authorization", "Basic "+c.UserColonPass)
86+
r, err := http.DefaultClient.Do(req)
87+
if err != nil {
88+
return "", err
89+
}
90+
defer CloseBody(r)
91+
if r.StatusCode >= 400 {
92+
ctx.SetState("creds", Creds{LoggedIn: false}).PersistWithEncryption()
93+
app.Window().Set("location", "login")
94+
}
95+
bytes, err := io.ReadAll(r.Body)
96+
if err != nil {
97+
return "", err
98+
}
99+
return string(bytes), nil
100+
}
101+
102+
func BasicAuth(username string, password string) string {
103+
return base64.StdEncoding.EncodeToString(
104+
[]byte(username + ":" + password),
105+
)
106+
}
107+
108+
func AuthorizedRequest(method string, url string,
109+
basicAuth string) (*http.Request, error) {
110+
req, err := http.NewRequest(method, url, nil)
111+
if err != nil {
112+
return nil, err
113+
}
114+
req.Header.Add("Authorization", "Basic "+basicAuth)
115+
return req, nil
116+
}

pwa/flush/browser.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package flush
2+
3+
import (
4+
"errors"
5+
"log"
6+
"time"
7+
8+
"github.com/maxence-charriere/go-app/v10/pkg/app"
9+
)
10+
11+
func SetLastUsedCredsState(ctx app.Context, user string, pass string) {
12+
ctx.SetState("lastUsedCredsRegister", LastTriedCreds{
13+
User: user,
14+
Password: pass,
15+
}).ExpiresIn(time.Second * 10)
16+
}
17+
18+
func ValidateRegistryCreds(
19+
user string, pass string, repeatPass string, lastCreds LastTriedCreds) error {
20+
if user == "" || pass == "" || repeatPass == "" {
21+
return errors.New("fill all required fields")
22+
}
23+
if pass != repeatPass {
24+
return errors.New("passwords don't match")
25+
}
26+
if lastCreds.User == user && lastCreds.Password == pass {
27+
log.Println("Skipping last used credentials...")
28+
return errors.New("you already tried those credentials")
29+
}
30+
return nil
31+
}
32+
33+
func ValidateLoginCreds(
34+
user string, pass string, lastCreds LastTriedCreds) error {
35+
if user == "" || pass == "" {
36+
return errors.New("username and password required")
37+
}
38+
if lastCreds.User == user && lastCreds.Password == pass {
39+
log.Println("Skipping last used credentials...")
40+
return errors.New("you already tried those credentials")
41+
}
42+
return nil
43+
}
44+
45+
func GetRegisterCreds() (string, string, string) {
46+
user := app.Window().GetElementByID("register-username").Get("value").String()
47+
pass := app.Window().GetElementByID("register-password").Get("value").String()
48+
repeatPass := app.Window().GetElementByID("register-password-repeat").Get("value").String()
49+
return user, pass, repeatPass
50+
}
51+
52+
func GetLoginCreds() (string, string) {
53+
user := app.Window().GetElementByID("username").Get("value").String()
54+
pass := app.Window().GetElementByID("password").Get("value").String()
55+
return user, pass
56+
}
57+
58+
func ShowBadRegisterCredsErr() {
59+
app.Window().GetElementByID(
60+
"register-error").Set(
61+
"innerHTML", "username: up to 60 chars, (letters, numbers and _),<br/>password: 8-60 chars")
62+
}
63+
64+
func DisplayError(err error) {
65+
app.Window().Call("alert", "An error occurred:\n\n"+err.Error()+"\n\nRefresh the page to continue.")
66+
log.Fatal(err)
67+
}
68+
69+
func ShowErrorDiv(ctx app.Context, err error, seconds time.Duration) {
70+
app.Window().GetElementByID("error").Set("innerHTML", err.Error())
71+
app.Window().GetElementByID("error").Set("className", ErrorDivCss)
72+
ctx.Async(func() {
73+
time.Sleep(time.Second * seconds)
74+
app.Window().GetElementByID("error").Set("className", InviCss)
75+
})
76+
// TODO consider using https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode
77+
// clone the element and set random ID to clone
78+
// then hide the clone after 2 seconds
79+
}

0 commit comments

Comments
 (0)