Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add an initial spike of playwright UI tests #1620

Merged
merged 2 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ ui/node_modules
Containerfile.*
dist
*-envs.json
.git/

# config
.auth-providers.yaml
Expand All @@ -14,6 +15,7 @@ dist
.gen-*
.*-lint
.generate
.git

#tests
./tests/*.json
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ jobs:
tags: quay.io/nexodus/nexd:latest
- name: envsubst
tags: quay.io/nexodus/envsubst:latest
- name: playwright
tags: quay.io/nexodus/playwright:latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
Expand Down Expand Up @@ -297,13 +299,20 @@ jobs:
name: envsubst
path: /tmp

- name: Download playwright image
uses: actions/download-artifact@v3
with:
name: playwright
path: /tmp

- name: Load Docker images
run: |
docker load --input /tmp/apiserver.tar
docker load --input /tmp/frontend.tar
docker load --input /tmp/ipam.tar
docker load --input /tmp/nexd.tar
docker load --input /tmp/envsubst.tar
docker load --input /tmp/playwright.tar

- name: Setup KIND
run: |
Expand Down
9 changes: 9 additions & 0 deletions Containerfile.playwright
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM mcr.microsoft.com/playwright:v1.39.0-jammy

ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/cacerts.crt

RUN apt-get update -y \
&& apt-get install -y libnss3-tools \
&& apt-get clean all

COPY --chmod=755 ./hack/update-ca.sh /update-ca.sh
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ dist/.generate: $(SWAGGER_YAML) dist/.ui-fmt docs/user-guide/nexd.md docs/user-g
$(CMD_PREFIX) touch $@

.PHONY: e2e
e2e: e2eprereqs dist/nexd dist/nexctl image-nexd ## Run e2e verbose tests
e2e: e2eprereqs dist/nexd dist/nexctl image-nexd image-playwright ## Run e2e verbose tests
CGO_ENABLED=1 gotestsum --format $(GOTESTSUM_FMT) -- \
-race --tags=integration ./integration-tests/... $(shell [ -z "$$NEX_TEST" ] || echo "-run $$NEX_TEST" )

Expand Down Expand Up @@ -610,6 +610,14 @@ image-envsubst:
docker build -f Containerfile.envsubst -t quay.io/nexodus/envsubst:$(TAG) .
docker tag quay.io/nexodus/envsubst:$(TAG) quay.io/nexodus/envsubst:latest

.PHONY: image-playwright ## Build the playwright image
image-playwright: dist/.image-playwright
dist/.image-playwright: Containerfile.playwright hack/update-ca.sh | dist
$(CMD_PREFIX) docker build -f Containerfile.playwright \
-t quay.io/nexodus/playwright:$(TAG) .
$(CMD_PREFIX) docker tag quay.io/nexodus/nexd:$(TAG) quay.io/nexodus/nexd:latest
$(CMD_PREFIX) touch $@

.PHONY: images
images: image-nexd image-frontend image-apiserver image-ipam image-envsubst ## Create container images

Expand Down
10 changes: 9 additions & 1 deletion hack/update-ca.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ set -e

if [ -f /.certs/rootCA.pem ]; then
if [ -x /usr/sbin/update-ca-certificates ]; then
cp /.certs/rootCA.pem /usr/local/share/ca-certificates/rootCA.crt
cp /.certs/rootCA.pem /usr/local/share/ca-certificates/cacerts.crt
/usr/sbin/update-ca-certificates 2> /dev/null > /dev/null

# the following is needed to get the playwright browsers to know about the root CA
if [ -x /usr/bin/certutil ]; then
mkdir -p $HOME/.pki/nssdb
/usr/bin/certutil --empty-password -d $HOME/.pki/nssdb -N
/usr/bin/certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n cacerts.crt -i /usr/local/share/ca-certificates/cacerts.crt
fi

elif [ -x /usr/bin/update-ca-trust ]; then
cp ./.certs/rootCA.pem /etc/pki/ca-trust/source/anchors/rootCA.crt
/usr/bin/update-ca-trust 2> /dev/null > /dev/null
Expand Down
10 changes: 10 additions & 0 deletions integration-tests/features/webui.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Feature: Web UI

Background:
Given a user named "Oscar" with password "testpass"
Given a user named "EvilBob" with password "testpass"

Scenario: Bob can login to the UI

Given I am logged in as "Oscar"
Then I run playwright script "./tests/login.spec.ts"
1 change: 1 addition & 0 deletions integration-tests/features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func TestFeatures(t *testing.T) {
s.Context = ctx
s.ApiURL = "https://api.try.nexodus.127.0.0.1.nip.io"
s.TlsConfig = tlsConfig
s.TestingT = t

status := godog.TestSuite{
Name: shortName,
Expand Down
109 changes: 109 additions & 0 deletions integration-tests/steps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ import (
"context"
"fmt"
"github.com/cucumber/godog"
"github.com/docker/docker/api/types/container"
"github.com/google/uuid"
"github.com/nexodus-io/nexodus/internal/cucumber"
"github.com/nexodus-io/nexodus/internal/wgcrypto"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"path"
"strings"
)

type extender struct {
Expand Down Expand Up @@ -34,6 +41,7 @@ func init() {
ctx.Step(`^I generate a new public key as \${([^}]*)}$`, e.iGenerateANewPublicKeyAsVariable)
ctx.Step(`^I generate a new key pair as \${([^}]*)}/\${([^}]*)}$`, e.iGenerateANewPublicKeyPairAsVariable)
ctx.Step(`^I decrypt the sealed "([^"]*)" with "([^"]*)" and store the result as \${([^}]*)}$`, e.iDeycryptTheSealedWithAndStoreTheResultAsDevice_bearer_token)
ctx.Step(`^I run playwright script "([^"]*)"$`, e.iRunPlaywrightScript)

ctx.After(func(ctx context.Context, sc *godog.Scenario, err error) (context.Context, error) {
if err == nil {
Expand Down Expand Up @@ -161,3 +169,104 @@ func (s *extender) iDeycryptTheSealedWithAndStoreTheResultAsDevice_bearer_token(
s.Variables[storeAs] = string(value)
return nil
}

func (s *extender) iRunPlaywrightScript(script string) error {
name := s.Suite.TestingT.Name() + "-" + uuid.New().String()
name = strings.ReplaceAll(name, "/", "-")

certsDir, err := findCertsDir()
require.NoError(s.Suite.TestingT, err)
projectDir := path.Join(certsDir, "..")

s.Suite.Mu.Lock()
user := s.Users[s.CurrentUser]
s.Suite.Mu.Unlock()

container, err := testcontainers.GenericContainer(s.Suite.Context, testcontainers.GenericContainerRequest{
ProviderType: providerType,
ContainerRequest: testcontainers.ContainerRequest{
// Too bad the following does not work on my mac..
//FromDockerfile: testcontainers.FromDockerfile{
// Context: projectDir,
// Dockerfile: "Containerfile.playwright",
// Repo: "quay.io",
// Tag: "nexodus/playwright:latest",
//},
Image: "quay.io/nexodus/playwright:latest",
Name: name,
Networks: []string{defaultNetwork},
HostConfigModifier: func(hostConfig *container.HostConfig) {
hostConfig.ExtraHosts = []string{
fmt.Sprintf("try.nexodus.127.0.0.1.nip.io:%s", hostDNSName),
fmt.Sprintf("api.try.nexodus.127.0.0.1.nip.io:%s", hostDNSName),
fmt.Sprintf("auth.try.nexodus.127.0.0.1.nip.io:%s", hostDNSName),
}
hostConfig.AutoRemove = false
},
Mounts: []testcontainers.ContainerMount{
{
Source: testcontainers.GenericBindMountSource{
HostPath: certsDir,
},
Target: "/.certs",
ReadOnly: true,
},
{
Source: testcontainers.GenericBindMountSource{
HostPath: path.Join(projectDir, "ui"),
},
Target: "/ui",
ReadOnly: false,
},
},
// User: fmt.Sprintf("%d:%d", os.Getuid(), os.Getgid()),
Env: map[string]string{
"CI": "true",
"NEXODUS_USERNAME": user.Subject,
"NEXODUS_PASSWORD": user.Password,
},
Cmd: []string{
"/update-ca.sh",
"/bin/bash",
"-c",
fmt.Sprintf("npm install && npx playwright test '%s'", script),
},
ConfigModifier: func(config *container.Config) {
config.WorkingDir = "/ui"
},
},
Started: true,
})
if err != nil {
return err
}
defer func() {
go func() {
_ = container.Terminate(s.Suite.Context)
}()
}()
container.FollowOutput(FnConsumer{
Apply: func(l testcontainers.Log) {
text := string(l.Content)
s.Logf("%s", text)
},
})
err = container.StartLogProducer(s.Suite.Context)
if err != nil {
return err
}

err = wait.ForExit().WaitUntilReady(s.Suite.Context, container)
if err != nil {
return err
}

state, err := container.State(s.Suite.Context)
if err != nil {
return err
}
if state.ExitCode != 0 {
return fmt.Errorf("playwright failed with exit code %d", state.ExitCode)
}
return nil
}
6 changes: 6 additions & 0 deletions internal/cucumber/cucumber.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"reflect"
"strings"
"sync"
"testing"
"time"

"github.com/cucumber/godog"
Expand Down Expand Up @@ -74,6 +75,7 @@ type TestSuite struct {
nextOrgId uint32
TlsConfig *tls.Config
DB *gorm.DB
TestingT *testing.T
}

// TestUser represents a user that can login to the system. The same users are shared by
Expand All @@ -98,6 +100,10 @@ type TestScenario struct {
hasTestCaseLock bool
}

func (s *TestScenario) Logf(format string, args ...any) {
s.Suite.TestingT.Logf(format, args...)
}

func (s *TestScenario) User() *TestUser {
s.Suite.Mu.Lock()
defer s.Suite.Mu.Unlock()
Expand Down
4 changes: 4 additions & 0 deletions ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@
.env.production.local

npm-debug.log*
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
64 changes: 60 additions & 4 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
},
"devDependencies": {
"@mui/material": "^5.14.17",
"@playwright/test": "^1.39.0",
"@types/jest": "^29.5.7",
"@types/node": "^20.9.0",
"@types/react": "^18.2.35",
"@types/react-dom": "^18.2.15",
"@vitejs/plugin-react": "^4.1.1",
Expand Down
Loading