github.com/saucelabs/saucectl@v0.175.1/internal/playwright/config_test.go (about) 1 package playwright 2 3 import ( 4 "errors" 5 "reflect" 6 "testing" 7 8 "github.com/stretchr/testify/assert" 9 "gotest.tools/v3/fs" 10 11 "github.com/saucelabs/saucectl/internal/config" 12 "github.com/saucelabs/saucectl/internal/insights" 13 "github.com/saucelabs/saucectl/internal/msg" 14 "github.com/saucelabs/saucectl/internal/saucereport" 15 ) 16 17 func Test_shardSuites(t *testing.T) { 18 type args struct { 19 suites []Suite 20 } 21 tests := []struct { 22 name string 23 args args 24 want []Suite 25 }{ 26 { 27 name: "shard into three", 28 args: args{[]Suite{{Name: "Test", NumShards: 3}}}, 29 want: []Suite{ 30 {Name: "Test (shard 1/3)", NumShards: 3, Params: SuiteConfig{Shard: "1/3"}}, 31 {Name: "Test (shard 2/3)", NumShards: 3, Params: SuiteConfig{Shard: "2/3"}}, 32 {Name: "Test (shard 3/3)", NumShards: 3, Params: SuiteConfig{Shard: "3/3"}}, 33 }, 34 }, 35 { 36 name: "shard some", 37 args: args{[]Suite{ 38 {Name: "Test", NumShards: 3}, 39 {Name: "Unsharded"}, 40 }}, 41 want: []Suite{ 42 {Name: "Test (shard 1/3)", NumShards: 3, Params: SuiteConfig{Shard: "1/3"}}, 43 {Name: "Test (shard 2/3)", NumShards: 3, Params: SuiteConfig{Shard: "2/3"}}, 44 {Name: "Test (shard 3/3)", NumShards: 3, Params: SuiteConfig{Shard: "3/3"}}, 45 {Name: "Unsharded"}, 46 }, 47 }, 48 { 49 name: "shard nothing", 50 args: args{[]Suite{ 51 {Name: "Test"}, 52 {Name: "Test", NumShards: 1}, 53 }}, 54 want: []Suite{ 55 {Name: "Test"}, 56 {Name: "Test", NumShards: 1}, 57 }, 58 }, 59 } 60 for _, tt := range tests { 61 t.Run(tt.name, func(t *testing.T) { 62 if got := shardSuitesByNumShards(tt.args.suites); !reflect.DeepEqual(got, tt.want) { 63 t.Errorf("shardSuites() = %v, want %v", got, tt.want) 64 } 65 }) 66 } 67 } 68 69 func TestShardSuites(t *testing.T) { 70 dir := fs.NewDir(t, "testcafe", 71 fs.WithDir("tests", 72 fs.WithMode(0755), 73 fs.WithDir("dir1", 74 fs.WithMode(0755), 75 fs.WithFile("example1.tests.js", "", fs.WithMode(0644)), 76 ), 77 fs.WithDir("dir2", 78 fs.WithMode(0755), 79 fs.WithFile("example2.tests.js", "", fs.WithMode(0644)), 80 ), 81 fs.WithDir("dir3", 82 fs.WithMode(0755), 83 fs.WithFile("example3.tests.js", "", fs.WithMode(0644)), 84 ), 85 ), 86 ) 87 defer dir.Remove() 88 89 testCases := []struct { 90 name string 91 p *Project 92 wantErr bool 93 expectedErrMsg string 94 expectedSuites []Suite 95 }{ 96 { 97 name: "numShards and shard can't be used at the same time", 98 p: &Project{Suites: []Suite{ 99 { 100 Name: "suite #1", 101 NumShards: 2, 102 Shard: "spec", 103 }, 104 }}, 105 wantErr: true, 106 expectedErrMsg: "suite name: suite #1 numShards and shard can't be used at the same time", 107 }, 108 { 109 name: "split by spec", 110 p: &Project{ 111 RootDir: dir.Path(), 112 Suites: []Suite{ 113 { 114 Name: "suite #1", 115 Shard: "spec", 116 TestMatch: []string{".*.js"}, 117 }, 118 }}, 119 wantErr: false, 120 expectedErrMsg: "", 121 expectedSuites: []Suite{ 122 { 123 Name: "suite #1 - tests/dir1/example1.tests.js", 124 TestMatch: []string{"tests/dir1/example1.tests.js"}, 125 Shard: "spec", 126 }, 127 { 128 Name: "suite #1 - tests/dir2/example2.tests.js", 129 TestMatch: []string{"tests/dir2/example2.tests.js"}, 130 Shard: "spec", 131 }, 132 { 133 Name: "suite #1 - tests/dir3/example3.tests.js", 134 TestMatch: []string{"tests/dir3/example3.tests.js"}, 135 Shard: "spec", 136 }, 137 }, 138 }, 139 { 140 name: "split by spec - no match", 141 p: &Project{ 142 RootDir: dir.Path(), 143 Suites: []Suite{ 144 { 145 Name: "suite #1", 146 Shard: "spec", 147 TestMatch: []string{"failing.*.js"}, 148 }, 149 }}, 150 wantErr: true, 151 expectedErrMsg: "suite 'suite #1' patterns have no matching files", 152 expectedSuites: []Suite{ 153 { 154 Name: "suite #1", 155 TestMatch: []string{"tests/dir1/example1.tests.js"}, 156 Shard: "spec", 157 }, 158 }, 159 }, 160 } 161 162 for _, tt := range testCases { 163 t.Run(tt.name, func(t *testing.T) { 164 err := ShardSuites(tt.p) 165 if tt.wantErr { 166 if err.Error() != tt.expectedErrMsg { 167 t.Errorf("ShardSuites() = %v, want %v", err.Error(), tt.expectedErrMsg) 168 } 169 } else { 170 assert.Equal(t, tt.expectedSuites, tt.p.Suites) 171 } 172 }) 173 } 174 } 175 176 func TestValidate(t *testing.T) { 177 testCases := []struct { 178 name string 179 p Project 180 wantErr bool 181 errMsg string 182 }{ 183 { 184 name: "missing version", 185 p: Project{Playwright: Playwright{Version: "v"}}, 186 wantErr: true, 187 errMsg: "missing framework version. Check available versions here: https://docs.saucelabs.com/dev/cli/saucectl/#supported-frameworks-and-browsers", 188 }, 189 { 190 name: "unable to locate the rootDir folder", 191 p: Project{ 192 Playwright: Playwright{Version: "v1.1.1"}, RootDir: "/test", 193 }, 194 wantErr: true, 195 errMsg: "unable to locate the rootDir folder /test", 196 }, 197 { 198 name: "not supported browser", 199 p: Project{ 200 Playwright: Playwright{Version: "v1.1.1"}, 201 Suites: []Suite{ 202 {Params: SuiteConfig{BrowserName: "ie"}}, 203 }}, 204 wantErr: true, 205 errMsg: "browserName: ie is not supported. List of supported browsers: chromium, firefox, webkit, chrome", 206 }, 207 { 208 name: msg.MissingRegion, 209 p: Project{ 210 Sauce: config.SauceConfig{Region: ""}, 211 Playwright: Playwright{Version: "v1.1.1"}, 212 Suites: []Suite{ 213 {Name: "suite #1", NumShards: 2, Params: SuiteConfig{BrowserName: "chromium"}}, 214 }}, 215 wantErr: true, 216 errMsg: "no sauce region set", 217 }, 218 } 219 220 for _, tt := range testCases { 221 t.Run(tt.name, func(t *testing.T) { 222 if tt.wantErr { 223 err := Validate(&tt.p) 224 if err.Error() != tt.errMsg { 225 t.Errorf("Validate() = %v, want %v", err.Error(), tt.errMsg) 226 } 227 } 228 }) 229 } 230 } 231 232 func TestPlaywright_SortByHistory(t *testing.T) { 233 testCases := []struct { 234 name string 235 suites []Suite 236 history insights.JobHistory 237 expRes []Suite 238 }{ 239 { 240 name: "sort suites by job history", 241 suites: []Suite{ 242 Suite{Name: "suite 1"}, 243 Suite{Name: "suite 2"}, 244 Suite{Name: "suite 3"}, 245 }, 246 history: insights.JobHistory{ 247 TestCases: []insights.TestCase{ 248 insights.TestCase{Name: "suite 2"}, 249 insights.TestCase{Name: "suite 1"}, 250 insights.TestCase{Name: "suite 3"}, 251 }, 252 }, 253 expRes: []Suite{ 254 Suite{Name: "suite 2"}, 255 Suite{Name: "suite 1"}, 256 Suite{Name: "suite 3"}, 257 }, 258 }, 259 { 260 name: "suites is the subset of job history", 261 suites: []Suite{ 262 Suite{Name: "suite 1"}, 263 Suite{Name: "suite 2"}, 264 }, 265 history: insights.JobHistory{ 266 TestCases: []insights.TestCase{ 267 insights.TestCase{Name: "suite 2"}, 268 insights.TestCase{Name: "suite 1"}, 269 insights.TestCase{Name: "suite 3"}, 270 }, 271 }, 272 expRes: []Suite{ 273 Suite{Name: "suite 2"}, 274 Suite{Name: "suite 1"}, 275 }, 276 }, 277 { 278 name: "job history is the subset of suites", 279 suites: []Suite{ 280 Suite{Name: "suite 1"}, 281 Suite{Name: "suite 2"}, 282 Suite{Name: "suite 3"}, 283 Suite{Name: "suite 4"}, 284 Suite{Name: "suite 5"}, 285 }, 286 history: insights.JobHistory{ 287 TestCases: []insights.TestCase{ 288 insights.TestCase{Name: "suite 2"}, 289 insights.TestCase{Name: "suite 1"}, 290 insights.TestCase{Name: "suite 3"}, 291 }, 292 }, 293 expRes: []Suite{ 294 Suite{Name: "suite 2"}, 295 Suite{Name: "suite 1"}, 296 Suite{Name: "suite 3"}, 297 Suite{Name: "suite 4"}, 298 Suite{Name: "suite 5"}, 299 }, 300 }, 301 } 302 303 for _, tc := range testCases { 304 t.Run(tc.name, func(t *testing.T) { 305 result := SortByHistory(tc.suites, tc.history) 306 for i := 0; i < len(result); i++ { 307 assert.Equal(t, tc.expRes[i].Name, result[i].Name) 308 } 309 }) 310 } 311 } 312 313 func TestPlaywright_FilterFailedTests(t *testing.T) { 314 testcases := []struct { 315 name string 316 suiteName string 317 report saucereport.SauceReport 318 project *Project 319 expResult string 320 expErr error 321 }{ 322 { 323 name: "it should set failed tests to specified suite", 324 suiteName: "my suite", 325 report: saucereport.SauceReport{ 326 Status: saucereport.StatusFailed, 327 Suites: []saucereport.Suite{ 328 { 329 Name: "my suite", 330 Status: saucereport.StatusFailed, 331 Tests: []saucereport.Test{ 332 { 333 Status: saucereport.StatusFailed, 334 Name: "failed test1", 335 }, 336 { 337 Status: saucereport.StatusFailed, 338 Name: "failed test2", 339 }, 340 }, 341 }, 342 }, 343 }, 344 project: &Project{ 345 Suites: []Suite{ 346 { 347 Name: "my suite", 348 }, 349 }, 350 }, 351 expResult: "failed test1|failed test2", 352 expErr: nil, 353 }, 354 { 355 name: "it should keep the original settings when suiteName doesn't exist in the project", 356 suiteName: "my suite2", 357 report: saucereport.SauceReport{ 358 Status: saucereport.StatusFailed, 359 Suites: []saucereport.Suite{ 360 { 361 Name: "my suite", 362 Status: saucereport.StatusFailed, 363 Tests: []saucereport.Test{ 364 { 365 Status: saucereport.StatusFailed, 366 Name: "failed test1", 367 }, 368 { 369 Status: saucereport.StatusFailed, 370 Name: "failed test2", 371 }, 372 }, 373 }, 374 }, 375 }, 376 project: &Project{ 377 Suites: []Suite{ 378 { 379 Name: "my suite", 380 }, 381 }, 382 }, 383 expResult: "", 384 expErr: errors.New("suite(my suite2) not found"), 385 }, 386 { 387 name: "it should keep the original settings when no failed test in SauceReport", 388 suiteName: "my suite", 389 report: saucereport.SauceReport{ 390 Status: saucereport.StatusPassed, 391 Suites: []saucereport.Suite{ 392 { 393 Name: "my suite", 394 Status: saucereport.StatusPassed, 395 Tests: []saucereport.Test{ 396 { 397 Status: saucereport.StatusPassed, 398 Name: "passed test1", 399 }, 400 { 401 Status: saucereport.StatusSkipped, 402 Name: "skipped test2", 403 }, 404 }, 405 }, 406 }, 407 }, 408 project: &Project{ 409 Suites: []Suite{ 410 { 411 Name: "my suite", 412 }, 413 }, 414 }, 415 expResult: "", 416 expErr: nil, 417 }, 418 } 419 for _, tc := range testcases { 420 t.Run(tc.name, func(t *testing.T) { 421 err := tc.project.FilterFailedTests(tc.suiteName, tc.report) 422 assert.Equal(t, tc.expErr, err) 423 assert.Equal(t, tc.expResult, tc.project.Suites[0].Params.Grep) 424 }) 425 } 426 }