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  }