github.com/kubeshop/testkube@v1.17.23/contrib/executor/playwright/pkg/runner/playwright.go (about) 1 package runner 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 9 "github.com/pkg/errors" 10 11 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 12 "github.com/kubeshop/testkube/pkg/envs" 13 "github.com/kubeshop/testkube/pkg/executor" 14 "github.com/kubeshop/testkube/pkg/executor/agent" 15 "github.com/kubeshop/testkube/pkg/executor/env" 16 "github.com/kubeshop/testkube/pkg/executor/output" 17 "github.com/kubeshop/testkube/pkg/executor/runner" 18 "github.com/kubeshop/testkube/pkg/executor/scraper" 19 "github.com/kubeshop/testkube/pkg/executor/scraper/factory" 20 "github.com/kubeshop/testkube/pkg/ui" 21 ) 22 23 func NewPlaywrightRunner(ctx context.Context, dependency string, params envs.Params) (*PlaywrightRunner, error) { 24 output.PrintLogf("%s Preparing test runner", ui.IconTruck) 25 26 var err error 27 r := &PlaywrightRunner{ 28 Params: params, 29 dependency: dependency, 30 } 31 32 r.Scraper, err = factory.TryGetScrapper(ctx, params) 33 if err != nil { 34 return nil, err 35 } 36 37 return r, nil 38 } 39 40 // PlaywrightRunner - implements runner interface used in worker to start test execution 41 type PlaywrightRunner struct { 42 Params envs.Params 43 Scraper scraper.Scraper 44 dependency string 45 } 46 47 var _ runner.Runner = &PlaywrightRunner{} 48 49 func (r *PlaywrightRunner) Run(ctx context.Context, execution testkube.Execution) (result testkube.ExecutionResult, err error) { 50 if r.Scraper != nil { 51 defer r.Scraper.Close() 52 } 53 output.PrintLogf("%s Preparing for test run", ui.IconTruck) 54 55 // check that the datadir exists 56 _, err = os.Stat(r.Params.DataDir) 57 if errors.Is(err, os.ErrNotExist) { 58 output.PrintLogf("%s Datadir %s does not exist", ui.IconCross, r.Params.DataDir) 59 return result, errors.Errorf("datadir not exist: %v", err) 60 } 61 62 runPath := filepath.Join(r.Params.DataDir, "repo", execution.Content.Repository.Path) 63 if execution.Content.Repository != nil && execution.Content.Repository.WorkingDir != "" { 64 runPath = filepath.Join(r.Params.DataDir, "repo", execution.Content.Repository.WorkingDir) 65 } 66 67 if _, err := os.Stat(filepath.Join(runPath, "package.json")); err == nil { 68 out, err := executor.Run(runPath, r.dependency, nil, "install") 69 if err != nil { 70 output.PrintLogf("%s Dependency installation error %s", ui.IconCross, r.dependency) 71 return result, errors.Errorf("%s install error: %v\n\n%s", r.dependency, err, out) 72 } 73 output.PrintLogf("%s Dependencies successfully installed", ui.IconBox) 74 } 75 76 var depManager, depCommand string 77 if r.dependency == "pnpm" { 78 depManager = "pnpm" 79 depCommand = "dlx" 80 } else { 81 depManager = "npx" 82 } 83 84 args := execution.Args 85 for i := range execution.Command { 86 if execution.Command[i] == "<depManager>" { 87 execution.Command[i] = depManager 88 } 89 } 90 91 for i := len(args) - 1; i >= 0; i-- { 92 if depCommand == "" && args[i] == "<depCommand>" { 93 args = append(args[:i], args[i+1:]...) 94 continue 95 } 96 97 if args[i] == "<depCommand>" { 98 args[i] = depCommand 99 } 100 101 args[i] = os.ExpandEnv(args[i]) 102 } 103 104 envManager := env.NewManagerWithVars(execution.Variables) 105 envManager.GetReferenceVars(envManager.Variables) 106 107 command, args := executor.MergeCommandAndArgs(execution.Command, args) 108 output.PrintEvent("Running", runPath, command, envManager.ObfuscateStringSlice(args)) 109 out, runErr := executor.Run(runPath, command, envManager, args...) 110 out = envManager.ObfuscateSecrets(out) 111 112 if runErr != nil { 113 output.PrintLogf("%s Test run failed", ui.IconCross) 114 result = testkube.ExecutionResult{ 115 Status: testkube.ExecutionStatusFailed, 116 OutputType: "text/plain", 117 Output: fmt.Sprintf("playwright test error: %s\n\n%s", runErr.Error(), out), 118 } 119 } else { 120 result = testkube.ExecutionResult{ 121 Status: testkube.ExecutionStatusPassed, 122 OutputType: "text/plain", 123 Output: string(out), 124 } 125 } 126 127 var rerr error 128 if execution.PostRunScript != "" && execution.ExecutePostRunScriptBeforeScraping { 129 output.PrintLog(fmt.Sprintf("%s Running post run script...", ui.IconCheckMark)) 130 131 if runPath == "" { 132 runPath = r.Params.WorkingDir 133 } 134 135 if rerr = agent.RunScript(execution.PostRunScript, runPath); rerr != nil { 136 output.PrintLogf("%s Failed to execute post run script %s", ui.IconWarning, rerr) 137 } 138 } 139 140 if r.Params.ScrapperEnabled { 141 reportFile := "playwright-report" 142 if err = scrapeArtifacts(ctx, r, execution, reportFile); err != nil { 143 return result, err 144 } 145 } 146 147 if rerr != nil { 148 return result, rerr 149 } 150 151 if runErr == nil { 152 output.PrintLogf("%s Test run successful", ui.IconCheckMark) 153 } 154 return result, runErr 155 } 156 157 // GetType returns runner type 158 func (r *PlaywrightRunner) GetType() runner.Type { 159 return runner.TypeMain 160 } 161 162 func scrapeArtifacts(ctx context.Context, r *PlaywrightRunner, execution testkube.Execution, reportName string) (err error) { 163 projectPath := filepath.Join(r.Params.DataDir, "repo", execution.Content.Repository.Path) 164 165 compressedName := reportName + "-zip" 166 if _, err := executor.Run(projectPath, "mkdir", nil, compressedName); err != nil { 167 output.PrintLogf("%s Artifact scraping failed: making dir %s", ui.IconCross, compressedName) 168 return errors.Errorf("mkdir error: %v", err) 169 } 170 171 if _, err := executor.Run(projectPath, "zip", nil, compressedName+"/"+reportName+".zip", "-r", reportName); err != nil { 172 output.PrintLogf("%s Artifact scraping failed: zipping reports %s", ui.IconCross, reportName) 173 return errors.Errorf("zip error: %v", err) 174 } 175 176 directories := []string{ 177 filepath.Join(projectPath, compressedName), 178 } 179 180 var masks []string 181 if execution.ArtifactRequest != nil { 182 directories = append(directories, execution.ArtifactRequest.Dirs...) 183 masks = execution.ArtifactRequest.Masks 184 } 185 186 output.PrintLogf("Scraping directories: %v with masks: %v", directories, masks) 187 188 if err := r.Scraper.Scrape(ctx, directories, masks, execution); err != nil { 189 return errors.Wrap(err, "error scraping artifacts") 190 } 191 192 return nil 193 }