working dagger

pull/107445/head
joshhunt 3 weeks ago
parent cea1f84437
commit 895bea7c52
  1. 2
      e2e-playwright/scenarios/login.spec.ts
  2. 11
      e2e-playwright/start-and-run-suite
  3. 146
      pkg/build/e2e-playwright/e2e.go
  4. 12
      pkg/build/e2e-playwright/main.go
  5. 79
      pkg/build/e2e-playwright/run-e2e.go
  6. 5
      playwright.config.ts

@ -1,7 +1,7 @@
import { test, expect } from '@grafana/plugin-e2e'; import { test, expect } from '@grafana/plugin-e2e';
test( test(
'Scenario test: Can login successfully', 'Scenario test: Can actually login successfully',
{ {
tag: ['@scenarios'], tag: ['@scenarios'],
}, },

@ -20,4 +20,13 @@ else
./scripts/grafana-server/wait-for-grafana ./scripts/grafana-server/wait-for-grafana
fi fi
PORT=3001 HOST=localhost yarn playwright test # Wait for health endpoint to be ready
echo "Waiting for Grafana health endpoint..."
while ! curl -s "http://localhost:3001/api/health" > /dev/null; do
sleep 1
done
echo "Grafana health endpoint is ready"
# PORT=3001 HOST=localhost yarn playwright test
PORT=3001 HOST=localhost yarn playwright test --grep @panels

@ -0,0 +1,146 @@
package main
import (
"context"
"encoding/json"
"fmt"
"strings"
"dagger.io/dagger"
)
type Deps struct {
NodeVersion string
PlaywrightVersion string
}
func GetVersions(ctx context.Context, src *dagger.Directory) (Deps, error) {
nvmrc, err := src.File(".nvmrc").Contents(ctx)
if err != nil {
return Deps{}, err
}
pkgJSON, err := src.File("package.json").Contents(ctx)
if err != nil {
return Deps{}, err
}
// parse JSON in-Go, no jq needed
var pkg struct {
DevDependencies map[string]string `json:"devDependencies"`
}
if err := json.Unmarshal([]byte(pkgJSON), &pkg); err != nil {
return Deps{}, err
}
return Deps{
NodeVersion: strings.TrimSpace(strings.TrimPrefix(nvmrc, "v")),
PlaywrightVersion: strings.TrimSpace(pkg.DevDependencies["@playwright/test"]),
}, nil
}
func RunTest(
ctx context.Context,
d *dagger.Client,
grafanaService *dagger.Service,
) (*dagger.Container, error) {
grafanaDir := "." // TODO: arg
nxCache := d.CacheVolume("nx-cache")
// 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",
"public/app/plugins/*/*/package.json",
"e2e/test-plugins/*/package.json",
".nvmrc",
},
})
// Files needed to run e2e tests. Above files 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?
"e2e/test-plugins",
"public/app/plugins", // TODO: do we need all of this?
// e2e files
"e2e-playwright",
"playwright.config.ts",
},
Exclude: []string{
"**/dist",
},
})
deps, err := GetVersions(ctx, yarnHostSrc)
if err != nil {
return nil, err
}
nodeBase := WithNode(d, deps.NodeVersion)
playwrightBase := WithPlaywright(d, nodeBase, deps.PlaywrightVersion)
e2eContainer := WithYarnInstall(d, playwrightBase, yarnHostSrc).
WithWorkdir("/src").
WithDirectory("/src", e2eHostSrc).
WithMountedCache(".nx", nxCache).
WithExec([]string{"yarn", "e2e:plugin:build"}).
WithEnvVariable("HOST", grafanaHost).
WithEnvVariable("PORT", fmt.Sprint(grafanaPort)).
WithServiceBinding(grafanaHost, grafanaService).
WithEnvVariable("PLAYWRIGHT_HTML_OPEN", "never").
WithExec([]string{"yarn", "e2e:playwright"}, dagger.ContainerWithExecOpts{
Expect: dagger.ReturnTypeAny,
})
// TODO: wrap in conditional arg
_, err = e2eContainer.Directory("/src/playwright-report").Export(ctx, "./dist/playwright-report")
if err != nil {
return nil, err
}
_, err = e2eContainer.Directory("/src/test-results").Export(ctx, "./dist/test-results")
if err != nil {
return nil, err
}
return e2eContainer, nil
}
func WithNode(d *dagger.Client, version string) *dagger.Container {
nodeImage := fmt.Sprintf("node:%s-slim", strings.TrimPrefix(version, "v"))
return d.Container().From(nodeImage)
}
func WithPlaywright(d *dagger.Client, base *dagger.Container, version string) *dagger.Container {
brCache := d.CacheVolume("playwright-browsers")
return base.
WithEnvVariable("PLAYWRIGHT_BROWSERS_PATH", "/playwright-cache").
WithMountedCache("/playwright-cache", brCache).
WithExec([]string{"npx", "-y", "playwright@" + version, "install", "--with-deps"})
}
func WithYarnInstall(d *dagger.Client, base *dagger.Container, yarnHostSrc *dagger.Directory) *dagger.Container {
yarnCache := d.CacheVolume("yarn-cache")
return base.
WithWorkdir("/src").
WithMountedCache("/.yarn", yarnCache).
WithEnvVariable("YARN_CACHE_FOLDER", "/.yarn").
// It's important to copy all files here because the whole src directory is then copied into the test runner container
WithDirectory("/src", yarnHostSrc).
WithExec([]string{"corepack", "enable"}).
WithExec([]string{"corepack", "install"}).
WithExec([]string{"yarn", "install", "--immutable"})
}

@ -118,26 +118,26 @@ func run(ctx context.Context, cmd *cli.Command) error {
c, runErr := RunTest(ctx, d, svc) c, runErr := RunTest(ctx, d, svc)
if runErr != nil { if runErr != nil {
return fmt.Errorf("failed to run a11y test suite: %w", runErr) return fmt.Errorf("failed to run e2e test suite: %w", runErr)
} }
c, syncErr := c.Sync(ctx) c, syncErr := c.Sync(ctx)
if syncErr != nil { if syncErr != nil {
return fmt.Errorf("failed to sync a11y test suite: %w", syncErr) return fmt.Errorf("failed to sync e2e test suite: %w", syncErr)
} }
code, codeErr := c.ExitCode(ctx) code, codeErr := c.ExitCode(ctx)
if codeErr != nil { if codeErr != nil {
return fmt.Errorf("failed to get exit code of a11y test suite: %w", codeErr) return fmt.Errorf("failed to get exit code of e2e test suite: %w", codeErr)
} }
if code == 0 { if code == 0 {
log.Printf("a11y tests passed with exit code %d", code) log.Printf("e2e tests passed with exit code %d", code)
} else { } else {
return fmt.Errorf("a11y tests failed with exit code %d", code) return fmt.Errorf("e2e tests failed with exit code %d", code)
} }
log.Println("a11y tests completed successfully") log.Println("e2e tests completed successfully")
return nil return nil
} }

@ -1,79 +0,0 @@
package main
import (
"context"
"fmt"
"strings"
"dagger.io/dagger"
)
// NodeVersionContainer returns a container whose `stdout` will return the node version from the '.nvmrc' file in the directory 'src'.
func NodeVersion(d *dagger.Client, src *dagger.File) *dagger.Container {
return d.Container().From("alpine:3").
WithMountedFile("/src/.nvmrc", src).
WithWorkdir("/src").
WithExec([]string{"cat", ".nvmrc"})
}
func NodeImage(version string) string {
return fmt.Sprintf("node:%s-slim", strings.TrimPrefix(strings.TrimSpace(version), "v"))
}
func RunTest(
ctx context.Context,
d *dagger.Client,
grafanaService *dagger.Service,
) (*dagger.Container, error) {
// Explicitly only the files u'sed by e2e tests
hostSrc := d.Host().Directory(".", dagger.HostDirectoryOpts{
Include: []string{
// Include all files for a valid yarn workspace install
"package.json",
"yarn.lock",
".yarnrc.yml",
".yarn",
"packages/*/package.json",
"public/app/plugins/*/*/package.json",
"e2e/test-plugins/*/package.json",
".nvmrc",
"public/app/types/*.d.ts",
// packages we use in playwright tests
"packages",
// e2e files
"e2e-playwright",
},
Exclude: []string{
"packages/*/dist",
},
})
nodeVersion, err := NodeVersion(d, hostSrc.File(".nvmrc")).Stdout(ctx)
if err != nil {
return nil, err
}
yarnCache := d.CacheVolume("yarn-cache")
yarnCacheDir := "/yarn-cache"
pa11yContainer := d.Container().From(NodeImage(nodeVersion)).
WithExec([]string{"npx", "-y", "playwright@1.52.0", "install", "--with-deps"}). // TODO: sync version from package.json
WithWorkdir("/src").
WithDirectory("/src", hostSrc).
WithMountedCache(yarnCacheDir, yarnCache).
WithExec([]string{"corepack", "enable"}).
WithExec([]string{"corepack", "install"}).
WithEnvVariable("YARN_CACHE_FOLDER", yarnCacheDir).
WithExec([]string{"yarn", "config", "get", "cacheFolder"}).
WithExec([]string{"yarn", "install", "--immutable"}).
// WithExec([]string{"yarn", "e2e:plugin:build"}).
WithEnvVariable("HOST", grafanaHost).
WithEnvVariable("PORT", fmt.Sprint(grafanaPort)).
WithExec([]string{"yarn", "packages:build"}).
WithExec([]string{"yarn", "e2e:playwright"})
return pa11yContainer, nil
}

@ -12,7 +12,10 @@ export default defineConfig<PluginOptions>({
retries: process.env.CI ? 2 : 0, retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined, workers: process.env.CI ? 1 : undefined,
reporter: 'html', reporter: [
['list'], // for terminal
['html'], // pretty
],
use: { use: {
baseURL: `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}`, baseURL: `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}`,
trace: 'retain-on-failure', trace: 'retain-on-failure',

Loading…
Cancel
Save