The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/build/e2e-playwright/main.go

242 lines
6.0 KiB

package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"dagger.io/dagger"
"github.com/urfave/cli/v3"
)
var (
grafanaPort = 3001
)
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
if err := NewApp().Run(ctx, os.Args); err != nil {
cancel()
fmt.Println(err)
os.Exit(1)
}
}
func NewApp() *cli.Command {
return &cli.Command{
Name: "a11y",
Usage: "Run Grafana playwright e2e tests",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "grafana-dir",
Usage: "Path to the grafana/grafana clone directory",
Value: ".",
Validator: mustBeDir("grafana-dir", false, false),
TakesFile: true,
},
&cli.StringFlag{
Name: "package",
Usage: "Path to the grafana tar.gz package",
Value: "grafana.tar.gz",
Validator: mustBeFile("package", false),
TakesFile: true,
},
&cli.StringFlag{
Name: "license",
Usage: "Path to the Grafana Enterprise license file (optional)",
Validator: mustBeFile("license", true),
TakesFile: true,
},
&cli.StringFlag{
Name: "shard",
Usage: "Test shard to run. See Playwright docs",
},
&cli.StringFlag{
Name: "results-dir",
Usage: "Path to the directory to export the playwright test results to (optional)",
Validator: mustBeDir("results-dir", true, true),
},
&cli.StringFlag{
Name: "html-dir",
Usage: "Enables the HTML reporter, exported to this directory (optional)",
Validator: mustBeDir("html-dir", true, true),
},
&cli.StringFlag{
Name: "blob-dir",
Usage: "Enables the blob reporter, exported to this directory. Useful with --shard (optional)",
Validator: mustBeDir("blob-dir", true, true),
},
},
Action: run,
}
}
func run(ctx context.Context, cmd *cli.Command) error {
grafanaDir := cmd.String("grafana-dir")
targzPath := cmd.String("package")
licensePath := cmd.String("license")
pwShard := cmd.String("shard")
resultsDir := cmd.String("results-dir")
htmlDir := cmd.String("html-dir")
blobDir := cmd.String("blob-dir")
// pa11yConfigPath := cmd.String("config")
// pa11yResultsPath := cmd.String("results")
// noThresholdFail := cmd.Bool("no-threshold-fail")
d, err := dagger.Connect(ctx)
if err != nil {
return fmt.Errorf("failed to connect to Dagger: %w", err)
}
// Explicitly only the files used by the grafana-server service
grafanaHostSrc := d.Host().Directory(grafanaDir, dagger.HostDirectoryOpts{
Include: []string{
"./devenv",
// Must build test plugins to run e2e tests
"./e2e-playwright/test-plugins",
"./packages/grafana-plugin-configs",
"./scripts/grafana-server/custom.ini",
"./scripts/grafana-server/start-server",
"./scripts/grafana-server/kill-server",
"./scripts/grafana-server/variables",
},
})
// Minimal files needed to run yarn install
yarnHostSrc := d.Host().Directory(grafanaDir, dagger.HostDirectoryOpts{
Include: []string{
"package.json",
"yarn.lock",
".yarnrc.yml",
".yarn",
"packages/*/package.json",
"packages/grafana-plugin-configs",
"public/app/plugins/*/*/package.json",
"e2e-playwright/test-plugins/*/package.json",
".nvmrc",
},
})
// Files needed to run e2e tests. yarnHostSrc will be copied into the test runner container as well.
e2eHostSrc := d.Host().Directory(".", dagger.HostDirectoryOpts{
Include: []string{
"public/app/types/*.d.ts",
"public/app/core/icons/cached.json",
// packages we use in playwright tests
"packages", // TODO: do we need all of this?
"public/app/plugins", // TODO: do we need all of this?
// e2e files
"e2e-playwright",
"e2e-playwright/test-plugins",
"playwright.config.ts",
},
Exclude: []string{
"**/dist",
},
})
frontendContainer, err := WithFrontendContainer(ctx, d, yarnHostSrc)
if err != nil {
return fmt.Errorf("failed to create frontend container: %w", err)
}
targz := d.Host().File(targzPath)
var license *dagger.File
if licensePath != "" {
license = d.Host().File(licensePath)
}
svc, err := GrafanaService(ctx, d, GrafanaServiceOpts{
HostSrc: grafanaHostSrc,
FrontendContainer: frontendContainer,
GrafanaTarGz: targz,
License: license,
})
if err != nil {
return fmt.Errorf("failed to create Grafana service: %w", err)
}
runOpts := RunTestOpts{
GrafanaService: svc,
FrontendContainer: frontendContainer,
HostSrc: e2eHostSrc,
Shard: pwShard,
TestResultsExportDir: resultsDir,
HTMLReportExportDir: htmlDir,
BlobReportExportDir: blobDir,
}
c, runErr := RunTest(ctx, d, runOpts)
if runErr != nil {
return fmt.Errorf("failed to run e2e test suite: %w", runErr)
}
c, syncErr := c.Sync(ctx)
if syncErr != nil {
return fmt.Errorf("failed to sync e2e test suite: %w", syncErr)
}
code, codeErr := c.ExitCode(ctx)
if codeErr != nil {
return fmt.Errorf("failed to get exit code of e2e test suite: %w", codeErr)
}
if code == 0 {
log.Printf("e2e tests passed with exit code %d", code)
} else {
return fmt.Errorf("e2e tests failed with exit code %d", code)
}
log.Println("e2e tests completed successfully")
return nil
}
func mustBeFile(arg string, emptyOk bool) func(string) error {
return func(s string) error {
if s == "" {
if emptyOk {
return nil
}
return cli.Exit(arg+" cannot be empty", 1)
}
stat, err := os.Stat(s)
if err != nil {
return cli.Exit(arg+" does not exist or cannot be read: "+s, 1)
}
if stat.IsDir() {
return cli.Exit(arg+" must be a file, not a directory: "+s, 1)
}
return nil
}
}
func mustBeDir(arg string, emptyOk bool, notExistOk bool) func(string) error {
return func(s string) error {
if s == "" {
if emptyOk {
return nil
}
return cli.Exit(arg+" cannot be empty", 1)
}
stat, err := os.Stat(s)
if err != nil {
if notExistOk {
return nil
}
return cli.Exit(arg+" does not exist or cannot be read: "+s, 1)
}
if !stat.IsDir() {
return cli.Exit(arg+" must be a directory: "+s, 1)
}
return nil
}
}