Skip to content

Commit

Permalink
🐹 feat(triggers): Playwright Engine (#3922)
Browse files Browse the repository at this point in the history
* feat(triggers): Playwright Engine

* cleanup

* updating dependency name

* adding playwright install

* adding playwright install
  • Loading branch information
xoscar authored Jul 3, 2024
1 parent 2838b1b commit 5890cc0
Show file tree
Hide file tree
Showing 15 changed files with 1,566 additions and 681 deletions.
1,455 changes: 814 additions & 641 deletions agent/proto/orchestrator.pb.go

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions agent/proto/orchestrator.proto
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ message Trigger {
GrpcRequest grpc = 3;
TraceIDRequest traceID = 4;
KafkaRequest kafka = 5;
PlaywrightEngineRequest playwrightEngine = 6;
}

message HttpRequest {
Expand Down Expand Up @@ -180,6 +181,7 @@ message TriggerResult {
TraceIdResponse traceID = 4;
KafkaResponse kafka = 5;
Error error = 6;
PlaywrightEngineResponse playwrightEngine = 7;
}

message HttpResponse {
Expand Down Expand Up @@ -398,3 +400,13 @@ message KafkaResponse {
string partition = 1;
string offset = 2;
}

message PlaywrightEngineRequest {
string target = 1;
string method = 2;
string script = 3;
}

message PlaywrightEngineResponse {
bool success = 1;
}
24 changes: 19 additions & 5 deletions agent/workers/trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func NewTriggerWorker(client *client.Client, opts ...TriggerOption) *TriggerWork
registry.Add(trigger.GRPC())
registry.Add(trigger.TRACEID())
registry.Add(trigger.KAFKA())
registry.Add(trigger.PLAYWRIGHTENGINE())

// Assign registry into worker
worker.registry = registry
Expand Down Expand Up @@ -207,11 +208,12 @@ func (w *TriggerWorker) trigger(ctx context.Context, triggerRequest *proto.Trigg

func convertProtoToTrigger(pt *proto.Trigger) trigger.Trigger {
return trigger.Trigger{
Type: trigger.TriggerType(pt.Type),
HTTP: convertProtoHttpTriggerToHttpTrigger(pt.Http),
GRPC: convertProtoGrpcTriggerToGrpcTrigger(pt.Grpc),
TraceID: convertProtoTraceIDTriggerToTraceIDTrigger(pt.TraceID),
Kafka: convertProtoKafkaTriggerToKafkaTrigger(pt.Kafka),
Type: trigger.TriggerType(pt.Type),
HTTP: convertProtoHttpTriggerToHttpTrigger(pt.Http),
GRPC: convertProtoGrpcTriggerToGrpcTrigger(pt.Grpc),
TraceID: convertProtoTraceIDTriggerToTraceIDTrigger(pt.TraceID),
Kafka: convertProtoKafkaTriggerToKafkaTrigger(pt.Kafka),
PlaywrightEngine: convertProtoPlaywrightEngineTriggerToPlaywrightEngineTrigger(pt.PlaywrightEngine),
}
}

Expand Down Expand Up @@ -307,6 +309,18 @@ func convertProtoTraceIDTriggerToTraceIDTrigger(traceIDRequest *proto.TraceIDReq
}
}

func convertProtoPlaywrightEngineTriggerToPlaywrightEngineTrigger(playwrightEngineRequest *proto.PlaywrightEngineRequest) *trigger.PlaywrightEngineRequest {
if playwrightEngineRequest == nil {
return nil
}

return &trigger.PlaywrightEngineRequest{
Script: playwrightEngineRequest.Script,
Target: playwrightEngineRequest.Target,
Method: playwrightEngineRequest.Method,
}
}

func convertProtoKafkaTriggerToKafkaTrigger(kafkaRequest *proto.KafkaRequest) *trigger.KafkaRequest {
if kafkaRequest == nil {
return nil
Expand Down
142 changes: 142 additions & 0 deletions agent/workers/trigger/playwrightengine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package trigger

import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
)

var (
node = "node"
app = "npx"
libName = "@tracetest/playwright-engine"
scriptPath = "script.js"
)

func PLAYWRIGHTENGINE() Triggerer {
return &playwrightTriggerer{}
}

type playwrightTriggerer struct{}

func (te *playwrightTriggerer) Trigger(ctx context.Context, triggerConfig Trigger, opts *Options) (Response, error) {
response := Response{
Result: TriggerResult{
Type: te.Type(),
PlaywrightEngine: &PlaywrightEngineResponse{
Success: false,
},
},
}

err := validate()
if err != nil {
return response, err
}

err = os.WriteFile(scriptPath, []byte(triggerConfig.PlaywrightEngine.Script), 0644)
if err != nil {
return response, err
}

err = start(opts.TraceID.String(), opts.SpanID.String(), triggerConfig.PlaywrightEngine.Target, triggerConfig.PlaywrightEngine.Method)
if err != nil {
os.Remove(scriptPath)
return response, err
}

os.Remove(scriptPath)
response.Result.PlaywrightEngine.Success = true
return response, err
}

func (t *playwrightTriggerer) Type() TriggerType {
return TriggerTypePlaywrightEngine
}

const TriggerTypePlaywrightEngine TriggerType = "playwrightengine"

func validate() error {
_, err := exec.LookPath(node)
if err != nil {
return fmt.Errorf("node not found in PATH")
}

_, err = exec.LookPath(app)
if err != nil {
return fmt.Errorf("npm not found in PATH")
}

return nil
}

func start(traceId, spanId, url, method string) error {
wd, err := os.Getwd()
if err != nil {
return err
}

res, err := execCommand(
app,
"playwright",
"install",
)

if err != nil {
return fmt.Errorf("error installing playwright: %s, %w", res, err)
}

path, err := filepath.Abs(fmt.Sprintf("%s/%s", wd, scriptPath))
if err != nil {
return err
}

res, err = execCommand(
app,
libName,
"--scriptPath",
path,
"--traceId",
traceId,
"--spanId",
spanId,
"--url",
url,
"--method",
method)

if err != nil {
return fmt.Errorf("error executing playwright engine: %s, %w", res, err)
}

return nil
}

func execCommand(name string, args ...string) (string, error) {
cmd := exec.Command(name, args...)

var out bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()

if err != nil {
return fmt.Sprint(err) + ": " + stderr.String(), err
}

return out.String(), nil
}

type PlaywrightEngineRequest struct {
Target string `json:"target,omitempty"`
Script string `json:"script,omitempty"`
Method string `json:"method,omitempty"`
}

type PlaywrightEngineResponse struct {
Success bool `json:"success"`
}
93 changes: 93 additions & 0 deletions agent/workers/trigger/playwrightengine_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package trigger_test

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/kubeshop/tracetest/agent/workers/trigger"
triggerer "github.com/kubeshop/tracetest/agent/workers/trigger"
"github.com/kubeshop/tracetest/server/pkg/id"
"github.com/stretchr/testify/assert"
)

func createOptions() *trigger.Options {
return &trigger.Options{
TraceID: id.NewRandGenerator().TraceID(),
SpanID: id.NewRandGenerator().SpanID(),
}
}

var script = `
const { expect } = require('@playwright/test');
async function basicTest(page) {
await expect(page.getByText('OK')).toBeVisible();
}
module.exports = { basicTest };
`

func TestTrigger(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {

assert.Equal(t, "GET", req.Method)

rw.WriteHeader(200)
_, err := rw.Write([]byte(`OK`))
assert.NoError(t, err)
}))
defer server.Close()

triggerConfig := trigger.Trigger{
Type: trigger.TriggerTypeHTTP,
PlaywrightEngine: &trigger.PlaywrightEngineRequest{
Target: server.URL,
Method: "basicTest",
Script: script,
},
}

ex := triggerer.PLAYWRIGHTENGINE()

resp, err := ex.Trigger(createContext(), triggerConfig, createOptions())
assert.NoError(t, err)

assert.Equal(t, true, resp.Result.PlaywrightEngine.Success)
}

var scriptFail = `
const { expect } = require('@playwright/test');
async function basicTest(page) {
await expect(page.getByText('NOT FOUND YORCH')).toBeVisible();
}
module.exports = { basicTest };
`

func TestTriggerFailure(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {

assert.Equal(t, "GET", req.Method)

rw.WriteHeader(200)
_, err := rw.Write([]byte(`OK`))
assert.NoError(t, err)
}))
defer server.Close()

triggerConfig := trigger.Trigger{
Type: trigger.TriggerTypeHTTP,
PlaywrightEngine: &trigger.PlaywrightEngineRequest{
Target: server.URL,
Method: "basicTest",
Script: scriptFail,
},
}

ex := triggerer.PLAYWRIGHTENGINE()

resp, err := ex.Trigger(createContext(), triggerConfig, createOptions())
assert.NotNil(t, err)

assert.Equal(t, false, resp.Result.PlaywrightEngine.Success)
}
24 changes: 13 additions & 11 deletions agent/workers/trigger/trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,22 @@ func (t TriggerType) IsIntegration() bool {

type (
Trigger struct {
Type TriggerType `json:"type"`
HTTP *HTTPRequest `json:"httpRequest,omitempty"`
GRPC *GRPCRequest `json:"grpc,omitempty"`
TraceID *TraceIDRequest `json:"traceid,omitempty"`
Kafka *KafkaRequest `json:"kafka,omitempty"`
Type TriggerType `json:"type"`
HTTP *HTTPRequest `json:"httpRequest,omitempty"`
GRPC *GRPCRequest `json:"grpc,omitempty"`
TraceID *TraceIDRequest `json:"traceid,omitempty"`
Kafka *KafkaRequest `json:"kafka,omitempty"`
PlaywrightEngine *PlaywrightEngineRequest `json:"playwrightEngine,omitempty"`
}

TriggerResult struct {
Type TriggerType `json:"type"`
HTTP *HTTPResponse `json:"http,omitempty"`
GRPC *GRPCResponse `json:"grpc,omitempty"`
TraceID *TraceIDResponse `json:"traceid,omitempty"`
Kafka *KafkaResponse `json:"kafka,omitempty"`
Error *TriggerError `json:"error,omitempty"`
Type TriggerType `json:"type"`
HTTP *HTTPResponse `json:"http,omitempty"`
GRPC *GRPCResponse `json:"grpc,omitempty"`
TraceID *TraceIDResponse `json:"traceid,omitempty"`
Kafka *KafkaResponse `json:"kafka,omitempty"`
PlaywrightEngine *PlaywrightEngineResponse `json:"playwrightEngine,omitempty"`
Error *TriggerError `json:"error,omitempty"`
}

TriggerError struct {
Expand Down
Loading

0 comments on commit 5890cc0

Please sign in to comment.