integration/ui: add UI integration tests
Introduce a new `integration/ui` package for integration tests. `integration/ui/uitest` contains a helper package for running browser tests.
This commit is contained in:
parent
962f81433f
commit
072dd24696
78
Jenkinsfile.ui
Normal file
78
Jenkinsfile.ui
Normal file
@ -0,0 +1,78 @@
|
||||
pipeline {
|
||||
agent {
|
||||
docker {
|
||||
label 'main'
|
||||
image docker.build("storj-ci", "--pull git://github.com/storj/ci.git#main").id
|
||||
args '-u root:root --cap-add SYS_PTRACE -v "/tmp/gomod":/go/pkg/mod -v "/tmp/npm":/npm --tmpfs "/tmp:exec,mode=777"'
|
||||
}
|
||||
}
|
||||
options {
|
||||
timeout(time: 36, unit: 'MINUTES')
|
||||
}
|
||||
environment {
|
||||
NPM_CONFIG_CACHE = '/npm/cache'
|
||||
GOTRACEBACK = 'all'
|
||||
COCKROACH_MEMPROF_INTERVAL=0
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
checkout scm
|
||||
|
||||
sh 'mkdir -p .build'
|
||||
|
||||
sh 'go mod download'
|
||||
sh 'service postgresql start'
|
||||
|
||||
dir(".build") {
|
||||
sh 'cockroach start-single-node --insecure --store=type=mem,size=2GiB --listen-addr=localhost:26256 --http-addr=localhost:8086 --cache 512MiB --max-sql-memory 512MiB --background'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stage('Verification') {
|
||||
stage('UI') {
|
||||
environment {
|
||||
STORJ_TEST_COCKROACH = 'cockroach://root@localhost:26256/testui?sslmode=disable'
|
||||
STORJ_TEST_POSTGRES = 'postgres://postgres@localhost/testui?sslmode=disable'
|
||||
STORJ_TEST_BROWSER = '/usr/bin/chromium'
|
||||
STORJ_TEST_SATELLITE_WEB = "${pwd()}/.build/satellite-web"
|
||||
|
||||
DISPLAY = ':99'
|
||||
}
|
||||
steps {
|
||||
sh 'cockroach sql --insecure --host=localhost:26256 -e \'create database testui;\''
|
||||
sh 'psql -U postgres -c \'create database testui;\''
|
||||
|
||||
sh 'cp -r ./web/satellite/ ${STORJ_TEST_SATELLITE_WEB}/'
|
||||
|
||||
// TODO: this is not quite correct
|
||||
sh 'mkdir ${STORJ_TEST_SATELLITE_WEB}/wasm'
|
||||
sh 'cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ${STORJ_TEST_SATELLITE_WEB}/wasm/wasm_exec.js'
|
||||
sh 'GOOS=js GOARCH=wasm go build -o ${STORJ_TEST_SATELLITE_WEB}/wasm/main.wasm storj.io/storj/satellite/console/wasm'
|
||||
|
||||
sh 'cd .build/satellite-web && npm install'
|
||||
sh 'cd .build/satellite-web && npm run build'
|
||||
|
||||
sh 'Xvfb -ac :99 -screen 0 1280x1024x16 &'
|
||||
sh 'go test -vet=off -race -json ./integration/ui/... 2>&1 | tee .build/ui-tests.json | xunit -out .build/ui-tests.xml'
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh script: 'cat .build/ui-tests.json | tparse -all -top -slow 100', returnStatus: true
|
||||
archiveArtifacts artifacts: '.build/ui-tests.json'
|
||||
junit '.build/ui-tests.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
sh "chmod -R 777 ." // ensure Jenkins agent can delete the working directory
|
||||
deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
1
go.mod
1
go.mod
@ -11,6 +11,7 @@ require (
|
||||
github.com/cheggaaa/pb/v3 v3.0.5
|
||||
github.com/fatih/color v1.9.0
|
||||
github.com/go-redis/redis/v8 v8.7.1
|
||||
github.com/go-rod/rod v0.100.0
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/google/go-cmp v0.5.4
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 // indirect
|
||||
|
12
go.sum
12
go.sum
@ -125,6 +125,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-redis/redis/v8 v8.7.1 h1:8IYi6RO83fNcG5amcUUYTN/qH2h4OjZHlim3KWGFSsA=
|
||||
github.com/go-redis/redis/v8 v8.7.1/go.mod h1:BRxHBWn3pO3CfjyX6vAoyeRmCquvxr6QG+2onGV2gYs=
|
||||
github.com/go-rod/rod v0.100.0 h1:tEKIb5wS3pGUpW4oJPYDxOKmRXaZbd6S+YVjJ6BHBBY=
|
||||
github.com/go-rod/rod v0.100.0/go.mod h1:h9igqSGReLmOWyHtdf0AtUd0mdkHFu3gFwBeV+stleM=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
@ -508,6 +510,16 @@ github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3/go.mod h1:R0Gbuw
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
|
||||
github.com/ysmood/goob v0.3.0 h1:XZ51cZJ4W3WCoCiUktixzMIQF86W7G5VFL4QQ/Q2uS0=
|
||||
github.com/ysmood/goob v0.3.0/go.mod h1:S3lq113Y91y1UBf1wj1pFOxeahvfKkCk6mTWTWbDdWs=
|
||||
github.com/ysmood/got v0.12.0 h1:Ol4cpy6Xdq1KCjPlWSA+tvekrnt9cV6LIw+Jvx0dj4M=
|
||||
github.com/ysmood/got v0.12.0/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY=
|
||||
github.com/ysmood/gotrace v0.2.2 h1:006KHGRThSRf8lwh4EyhNmuuq/l+Ygs+JqojkhEG1/E=
|
||||
github.com/ysmood/gotrace v0.2.2/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM=
|
||||
github.com/ysmood/gson v0.6.4 h1:Yb6tosv6bk59HqjZu2/7o4BFherpYEMkDkXmlhgryZ4=
|
||||
github.com/ysmood/gson v0.6.4/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
|
||||
github.com/ysmood/leakless v0.7.0 h1:XCGdaPExyoreoQd+H5qgxM3ReNbSPFsEXpSKwbXbwQw=
|
||||
github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
|
||||
|
33
integration/ui/satellite/user_login_test.go
Normal file
33
integration/ui/satellite/user_login_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package satellite_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/input"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"storj.io/common/testcontext"
|
||||
"storj.io/storj/integration/ui/uitest"
|
||||
"storj.io/storj/private/testplanet"
|
||||
)
|
||||
|
||||
func TestLoginToAccount(t *testing.T) {
|
||||
uitest.Run(t, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet, browser *rod.Browser) {
|
||||
loginPageURL := planet.Satellites[0].ConsoleURL() + "/login"
|
||||
user := planet.Uplinks[0].User[planet.Satellites[0].ID()]
|
||||
|
||||
page := browser.Timeout(10 * time.Second).MustPage(loginPageURL)
|
||||
page.MustSetViewport(1350, 600, 1, false)
|
||||
page.MustElement(".headerless-input").MustInput(user.Email)
|
||||
page.MustElement("[type=password]").MustInput(user.Password)
|
||||
page.Keyboard.MustPress(input.Enter)
|
||||
|
||||
dashboardTitle := page.MustElement(".dashboard-area__header-wrapper__title").MustText()
|
||||
require.Contains(t, dashboardTitle, "Dashboard")
|
||||
})
|
||||
}
|
92
integration/ui/uitest/run.go
Normal file
92
integration/ui/uitest/run.go
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package uitest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/launcher"
|
||||
"github.com/go-rod/rod/lib/utils"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zaptest"
|
||||
|
||||
"storj.io/common/testcontext"
|
||||
"storj.io/storj/private/testplanet"
|
||||
"storj.io/storj/satellite"
|
||||
)
|
||||
|
||||
// Test defines common services for uitests.
|
||||
type Test func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet, browser *rod.Browser)
|
||||
|
||||
type zapWriter struct {
|
||||
*zap.Logger
|
||||
}
|
||||
|
||||
func (log zapWriter) Write(data []byte) (int, error) {
|
||||
log.Logger.Info(string(data))
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// Run starts a new UI test.
|
||||
func Run(t *testing.T, test Test) {
|
||||
if os.Getenv("STORJ_TEST_SATELLITE_WEB") == "" {
|
||||
t.Skip("Enable UI tests by setting STORJ_TEST_SATELLITE_WEB to built npm")
|
||||
}
|
||||
if os.Getenv("STORJ_TEST_SATELLITE_WEB") == "omit" {
|
||||
return
|
||||
}
|
||||
|
||||
testplanet.Run(t, testplanet.Config{
|
||||
SatelliteCount: 1, StorageNodeCount: 4, UplinkCount: 1,
|
||||
Reconfigure: testplanet.Reconfigure{
|
||||
Satellite: func(log *zap.Logger, index int, config *satellite.Config) {
|
||||
config.Console.StaticDir = os.Getenv("STORJ_TEST_SATELLITE_WEB")
|
||||
},
|
||||
},
|
||||
NonParallel: true,
|
||||
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||
showBrowser := os.Getenv("STORJ_TEST_SHOW_BROWSER") != ""
|
||||
|
||||
logLauncher := zaptest.NewLogger(t).Named("launcher")
|
||||
|
||||
launch := launcher.New().
|
||||
Headless(!showBrowser).
|
||||
Leakless(false).
|
||||
Devtools(false).
|
||||
NoSandbox(true).
|
||||
UserDataDir(ctx.Dir("browser")).
|
||||
Logger(zapWriter{Logger: logLauncher})
|
||||
|
||||
if browserBin := os.Getenv("STORJ_TEST_BROWSER"); browserBin != "" {
|
||||
launch = launch.Bin(browserBin)
|
||||
}
|
||||
|
||||
defer launch.Cleanup()
|
||||
|
||||
url, err := launch.Launch()
|
||||
require.NoError(t, err)
|
||||
|
||||
logBrowser := zaptest.NewLogger(t).Named("rod")
|
||||
|
||||
browser := rod.New().
|
||||
Timeout(time.Minute).
|
||||
ControlURL(url).
|
||||
SlowMotion(300 * time.Millisecond).
|
||||
Logger(utils.Log(func(msg ...interface{}) {
|
||||
logBrowser.Info(fmt.Sprintln(msg...))
|
||||
})).
|
||||
Context(ctx).
|
||||
WithPanic(func(v interface{}) { require.Fail(t, "check failed", v) })
|
||||
defer ctx.Check(browser.Close)
|
||||
|
||||
require.NoError(t, browser.Connect())
|
||||
|
||||
test(t, ctx, planet, browser)
|
||||
})
|
||||
}
|
20
integration/ui/uitest/run_test.go
Normal file
20
integration/ui/uitest/run_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package uitest_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
|
||||
"storj.io/common/testcontext"
|
||||
"storj.io/storj/integration/ui/uitest"
|
||||
"storj.io/storj/private/testplanet"
|
||||
)
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
uitest.Run(t, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet, browser *rod.Browser) {
|
||||
t.Log("working")
|
||||
})
|
||||
}
|
@ -185,6 +185,11 @@ func (system *Satellite) Addr() string { return system.API.Server.Addr().String(
|
||||
// URL returns the node url from the Satellite system API.
|
||||
func (system *Satellite) URL() string { return system.NodeURL().String() }
|
||||
|
||||
// ConsoleURL returns the console URL.
|
||||
func (system *Satellite) ConsoleURL() string {
|
||||
return "http://" + system.API.Console.Listener.Addr().String()
|
||||
}
|
||||
|
||||
// NodeURL returns the storj.NodeURL from the Satellite system API.
|
||||
func (system *Satellite) NodeURL() storj.NodeURL {
|
||||
return storj.NodeURL{ID: system.API.ID(), Address: system.API.Addr()}
|
||||
|
@ -21,6 +21,7 @@ func TestSatellite_AddProject(t *testing.T) {
|
||||
user, err := planet.Satellites[0].AddUser(ctx, console.CreateUser{
|
||||
FullName: "test user",
|
||||
Email: "test-email@test",
|
||||
Password: "password",
|
||||
}, 4)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -39,6 +39,7 @@ type Uplink struct {
|
||||
|
||||
APIKey map[storj.NodeID]*macaroon.APIKey
|
||||
Access map[storj.NodeID]*uplink.Access
|
||||
User map[storj.NodeID]UserLogin
|
||||
|
||||
// Projects is indexed by the satellite number.
|
||||
Projects []*Project
|
||||
@ -63,6 +64,12 @@ type ProjectOwner struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
// UserLogin contains information about the user login.
|
||||
type UserLogin struct {
|
||||
Email string
|
||||
Password string
|
||||
}
|
||||
|
||||
// DialMetainfo dials the satellite with the appropriate api key.
|
||||
func (project *Project) DialMetainfo(ctx context.Context) (*metaclient.Client, error) {
|
||||
return project.client.DialMetainfo(ctx, project.Satellite, project.RawAPIKey)
|
||||
@ -106,6 +113,7 @@ func (planet *Planet) newUplink(ctx context.Context, name string) (*Uplink, erro
|
||||
Identity: identity,
|
||||
APIKey: map[storj.NodeID]*macaroon.APIKey{},
|
||||
Access: map[storj.NodeID]*uplink.Access{},
|
||||
User: map[storj.NodeID]UserLogin{},
|
||||
}
|
||||
|
||||
planetUplink.Log.Debug("id=" + identity.ID.String())
|
||||
@ -124,6 +132,11 @@ func (planet *Planet) newUplink(ctx context.Context, name string) (*Uplink, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
planetUplink.User[satellite.ID()] = UserLogin{
|
||||
Email: user.Email,
|
||||
Password: user.FullName,
|
||||
}
|
||||
|
||||
project, err := satellite.AddProject(ctx, user.ID, projectName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
Loading…
Reference in New Issue
Block a user