@toptal/davinci-qa 12.0.2-alpha-fx-2996-add-error-message-for-missing-engine.12 → 12.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Change Log
2
2
 
3
+ ## 12.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1541](https://github.com/toptal/davinci/pull/1541) [`397a53c9`](https://github.com/toptal/davinci/commit/397a53c9add9f7e8d485648b51cd22676d97c78f) Thanks [@TomasSlama](https://github.com/TomasSlama)! - ---
8
+ - Update documentation about prefering individual davinci packages usage
9
+ - Updated dependencies [[`397a53c9`](https://github.com/toptal/davinci/commit/397a53c9add9f7e8d485648b51cd22676d97c78f)]:
10
+ - @toptal/davinci-engine@7.4.6
11
+
3
12
  ## 12.0.1
4
13
 
5
14
  ### Patch Changes
package/README.md CHANGED
@@ -4,7 +4,7 @@ Run tests in your projects with almost 0 configuration.
4
4
 
5
5
  ## Installation
6
6
 
7
- Use it by installing `yarn add @toptal/davinci` in your project.
7
+ Use it by installing `yarn add @toptal/davinci-qa` in your project.
8
8
 
9
9
  ## Usage
10
10
 
@@ -17,8 +17,8 @@ Use it by installing `yarn add @toptal/davinci` in your project.
17
17
  ### Alias
18
18
 
19
19
  You can use davinci qa both as a standalone command `davinci-qa [...args]`
20
- or as a sub-command of the davinci command: `davinci qa [args...]`.
21
- Recommended way is to use davinci by parts, i.e. `davinci-qa unit`.
20
+ or as a sub-command of the davinci command: `davinci qa [args...]`, in this case @toptal/davinci package is required.
21
+ Recommended way is to use individual davinci packages, i.e. `davinci-qa unit`.
22
22
 
23
23
 
24
24
  ### IDE Tooling
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@toptal/davinci-qa",
3
+ "version": "12.0.2",
4
+ "description": "QA package to test your application",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "keywords": [
9
+ "qa",
10
+ "testing"
11
+ ],
12
+ "author": "Toptal",
13
+ "homepage": "https://github.com/toptal/davinci/tree/master/packages/qa#readme",
14
+ "license": "ISC",
15
+ "bin": {
16
+ "davinci-qa": "./bin/davinci-qa.js"
17
+ },
18
+ "main": "./src/index.js",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/toptal/davinci.git"
22
+ },
23
+ "scripts": {
24
+ "build:package": "../../bin/build-package.js",
25
+ "prepublishOnly": "../../bin/prepublish.js",
26
+ "test": "echo \"Error: run tests from root\" && exit 1"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/toptal/davinci/issues"
30
+ },
31
+ "dependencies": {
32
+ "@babel/core": "^7.18.0",
33
+ "@babel/preset-env": "^7.18.0",
34
+ "@babel/preset-react": "^7.17.12",
35
+ "@babel/preset-typescript": "^7.17.12",
36
+ "@cypress/code-coverage": "^3.10.0",
37
+ "@cypress/webpack-preprocessor": "^5.12.0",
38
+ "@testing-library/jest-dom": "^5.16.4",
39
+ "@testing-library/react": "^12.1.2",
40
+ "@toptal/davinci-cli-shared": "1.6.0",
41
+ "@types/jest": "^27.5.1",
42
+ "babel-jest": "^28.1.3",
43
+ "cypress": "^10.0.3",
44
+ "enhanced-resolve": "^5.9.2",
45
+ "fs-extra": "^10.0.0",
46
+ "glob": "^8.0.3",
47
+ "happo-cypress": "^3.0.1",
48
+ "happo-e2e": "^1.2.0",
49
+ "happo-plugin-storybook": "^3.0.0",
50
+ "happo.io": "^7.2.1",
51
+ "jest": "^28.1.0",
52
+ "jest-environment-jsdom": "^28.1.0",
53
+ "jest-html-reporters": "^3.0.10",
54
+ "jest-junit": "^13.1.0",
55
+ "jest-silent-reporter": "^0.5.0",
56
+ "jest-styled-components": "^7.0.8",
57
+ "jsdom": "^19.0.0",
58
+ "matchmedia-polyfill": "^0.3.2",
59
+ "semver": "^7.3.2"
60
+ },
61
+ "devDependencies": {
62
+ "json5": "^2.2.1",
63
+ "mocha": "^10.0.0"
64
+ },
65
+ "peerDependencies": {
66
+ "@toptal/davinci-engine": "7.4.6"
67
+ }
68
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toptal/davinci-qa",
3
- "version": "12.0.2-alpha-fx-2996-add-error-message-for-missing-engine.12+227d5ea7",
3
+ "version": "12.0.2",
4
4
  "description": "QA package to test your application",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -37,7 +37,7 @@
37
37
  "@cypress/webpack-preprocessor": "^5.12.0",
38
38
  "@testing-library/jest-dom": "^5.16.4",
39
39
  "@testing-library/react": "^12.1.2",
40
- "@toptal/davinci-cli-shared": "1.6.1-alpha-fx-2996-add-error-message-for-missing-engine.21+227d5ea7",
40
+ "@toptal/davinci-cli-shared": "1.6.0",
41
41
  "@types/jest": "^27.5.1",
42
42
  "babel-jest": "^28.1.3",
43
43
  "cypress": "^10.0.3",
@@ -63,7 +63,6 @@
63
63
  "mocha": "^10.0.0"
64
64
  },
65
65
  "peerDependencies": {
66
- "@toptal/davinci-engine": "3 - 7"
67
- },
68
- "gitHead": "227d5ea750c2b2ce9f4abd1ff33bbe1838ae9d28"
66
+ "@toptal/davinci-engine": "7.4.6"
67
+ }
69
68
  }
@@ -2,7 +2,6 @@ const {
2
2
  runSync,
3
3
  print,
4
4
  convertToCLIParameters,
5
- packageUtils,
6
5
  } = require('@toptal/davinci-cli-shared')
7
6
 
8
7
  const getCypressConfigPath = require('../utils/get-cypress-config-path')
@@ -11,17 +10,6 @@ const integrationOpenCommand = ({
11
10
  options: { baseUrl, ...cypressOptions } = {},
12
11
  files: testFiles = [],
13
12
  }) => {
14
- const prerequirementsError = packageUtils.checkPrerequirement({
15
- dependencies:
16
- packageUtils.getPackagePackageJson(__dirname).peerDependencies,
17
- packageName: '@toptal/davinci-engine',
18
- })
19
-
20
- if (prerequirementsError) {
21
- print.red(prerequirementsError)
22
- process.exit(1)
23
- }
24
-
25
13
  print.green('Running integration tests...')
26
14
 
27
15
  const configPath = getCypressConfigPath()
@@ -3,7 +3,6 @@ const {
3
3
  print,
4
4
  convertToCLIParameters,
5
5
  files,
6
- packageUtils,
7
6
  } = require('@toptal/davinci-cli-shared')
8
7
 
9
8
  const getCypressConfigPath = require('../utils/get-cypress-config-path')
@@ -12,17 +11,6 @@ const integrationRunCommand = ({
12
11
  options: { baseUrl, anvilTag, ...cypressOptions } = {},
13
12
  files: testFiles = [],
14
13
  }) => {
15
- const prerequirementsError = packageUtils.checkPrerequirement({
16
- dependencies:
17
- packageUtils.getPackagePackageJson(__dirname).peerDependencies,
18
- packageName: '@toptal/davinci-engine',
19
- })
20
-
21
- if (prerequirementsError) {
22
- print.red(prerequirementsError)
23
- process.exit(1)
24
- }
25
-
26
14
  print.green('Running integration tests...')
27
15
 
28
16
  const configPath = getCypressConfigPath()
@@ -0,0 +1,115 @@
1
+ /* eslint-disable import/order -- changing the order messes up with jest mocking */
2
+ const fs = require('fs')
3
+
4
+ const originalExistsSync = fs.existsSync
5
+
6
+ const getPackageFilePathMock = jest.fn()
7
+ const getProjectRootFilePathMock = jest.fn(path => `mock:${path}`)
8
+ const fsExistsSyncSpy = jest.spyOn(fs, 'existsSync')
9
+
10
+ jest.mock('@toptal/davinci-cli-shared', () => ({
11
+ ...jest.requireActual('@toptal/davinci-cli-shared'),
12
+ runSync: jest.fn(),
13
+ print: {
14
+ red: jest.fn(),
15
+ green: jest.fn(),
16
+ },
17
+ files: {
18
+ getPackageFilePath: getPackageFilePathMock,
19
+ getProjectRootFilePath: getProjectRootFilePathMock,
20
+ },
21
+ }))
22
+
23
+ const { runSync } = require('@toptal/davinci-cli-shared')
24
+
25
+ const { action } = require('./integration-run')
26
+
27
+ describe('integrationRunCommand', () => {
28
+ beforeEach(() => {
29
+ jest.clearAllMocks()
30
+ })
31
+
32
+ it('runs cypress with default options', () => {
33
+ const localConfig = 'mock:cypress.config.js'
34
+ const davinciConfig = 'packages/qa/src/configs/cypress.config.js'
35
+
36
+ getPackageFilePathMock.mockReturnValueOnce(davinciConfig)
37
+
38
+ // fs.existsSync is used in a lot of places, even internally by jest
39
+ // we want to only mock when asking for our files
40
+ fsExistsSyncSpy.mockImplementation((path, ...rest) =>
41
+ path === localConfig ? false : originalExistsSync(path, ...rest)
42
+ )
43
+
44
+ action({ options: {} })
45
+
46
+ expect(runSync).toHaveBeenCalledWith('yarn', [
47
+ 'cypress',
48
+ 'run',
49
+ '--config-file',
50
+ davinciConfig,
51
+ ])
52
+
53
+ expect(getPackageFilePathMock).toHaveBeenCalledWith(
54
+ '@toptal/davinci-qa',
55
+ 'src/configs/cypress.config.js'
56
+ )
57
+ })
58
+
59
+ it('run cypress with local config, if present', () => {
60
+ const localConfig = 'mock:cypress.config.js'
61
+ const davinciConfig = 'packages/qa/src/configs/cypress.config.js'
62
+
63
+ getPackageFilePathMock.mockReturnValueOnce(davinciConfig)
64
+
65
+ // fs.existsSync is used in a lot of places, even internally by jest
66
+ // we want to only mock when asking for our files
67
+ fsExistsSyncSpy.mockImplementation((path, ...rest) =>
68
+ path === localConfig ? true : originalExistsSync(path, ...rest)
69
+ )
70
+
71
+ action({ options: {} })
72
+
73
+ expect(runSync).toHaveBeenCalledWith('yarn', [
74
+ 'cypress',
75
+ 'run',
76
+ '--config-file',
77
+ localConfig,
78
+ ])
79
+
80
+ expect(getPackageFilePathMock).not.toHaveBeenCalledWith(
81
+ '@toptal/davinci-qa',
82
+ 'src/configs/cypress.config.js'
83
+ )
84
+ })
85
+
86
+ it('runs cypress with custom options', () => {
87
+ const localConfig = 'mock:cypress.config.js'
88
+ const davinciConfig = 'packages/qa/src/configs/cypress.config.js'
89
+ const spec = 'cypress/integration/activation_flow.spec.ts'
90
+
91
+ getPackageFilePathMock.mockReturnValueOnce(davinciConfig)
92
+
93
+ // fs.existsSync is used in a lot of places, even internally by jest
94
+ // we want to only mock when asking for our files
95
+ fsExistsSyncSpy.mockImplementation((path, ...rest) =>
96
+ path === localConfig ? false : originalExistsSync(path, ...rest)
97
+ )
98
+
99
+ action({ options: { spec } })
100
+
101
+ expect(runSync).toHaveBeenCalledWith('yarn', [
102
+ 'cypress',
103
+ 'run',
104
+ '--config-file',
105
+ davinciConfig,
106
+ '--spec',
107
+ spec,
108
+ ])
109
+
110
+ expect(getPackageFilePathMock).toHaveBeenCalledWith(
111
+ '@toptal/davinci-qa',
112
+ 'src/configs/cypress.config.js'
113
+ )
114
+ })
115
+ })
@@ -0,0 +1,213 @@
1
+ const jestRunCLIMock = jest
2
+ .fn()
3
+ .mockResolvedValue({ results: { success: true } })
4
+ const getPackageFilePathMock = jest.fn()
5
+ const getProjectRootFilePathMock = jest.fn()
6
+ const getProjectRootFileContentMock = jest.fn().mockReturnValue({
7
+ devDependencies: {},
8
+ dependencies: {},
9
+ })
10
+
11
+ jest.mock('@toptal/davinci-cli-shared', () => ({
12
+ runSync: jest.fn(),
13
+ print: {
14
+ green: jest.fn(),
15
+ grey: jest.fn(),
16
+ red: jest.fn(),
17
+ },
18
+ files: {
19
+ getPackageFilePath: getPackageFilePathMock,
20
+ getProjectRootFileContent: getProjectRootFileContentMock,
21
+ getProjectRootFilePath: getProjectRootFilePathMock,
22
+ },
23
+ }))
24
+
25
+ jest.mock('jest', () => ({
26
+ runCLI: jestRunCLIMock,
27
+ }))
28
+
29
+ const { action } = require('./unit-tests')
30
+ const originalEnv = process.env
31
+
32
+ describe('unitTestsCommand', () => {
33
+ beforeEach(() => {
34
+ jest.clearAllMocks()
35
+ })
36
+
37
+ afterEach(() => {
38
+ process.env = originalEnv
39
+ })
40
+
41
+ describe('when running locally', () => {
42
+ beforeEach(() => {
43
+ delete process.env.CI
44
+ })
45
+
46
+ it('calls the jest CLI without additional reporters', async () => {
47
+ const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
48
+
49
+ getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
50
+
51
+ await action({})
52
+
53
+ expect(jestRunCLIMock).toHaveBeenCalledWith(
54
+ { config: davinciJestConfigPath, testPathPattern: [] },
55
+ expect.anything()
56
+ )
57
+ })
58
+ })
59
+
60
+ describe('when running on CI', () => {
61
+ beforeEach(() => {
62
+ process.env.CI = true
63
+ })
64
+
65
+ it('calls the jest CLI with default Davinci reporters', async () => {
66
+ const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
67
+
68
+ getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
69
+
70
+ await action({})
71
+
72
+ expect(jestRunCLIMock).toHaveBeenCalledWith(
73
+ expect.objectContaining({
74
+ reporters: [
75
+ [
76
+ 'jest-silent-reporter',
77
+ {
78
+ useDots: true,
79
+ showPaths: true,
80
+ showWarnings: true,
81
+ },
82
+ ],
83
+ [
84
+ 'jest-junit',
85
+ {
86
+ outputDirectory: 'reports',
87
+ },
88
+ ],
89
+ [
90
+ 'jest-html-reporters',
91
+ {
92
+ publicPath: './reports',
93
+ },
94
+ ],
95
+ ],
96
+ }),
97
+ expect.anything()
98
+ )
99
+ })
100
+
101
+ it('returns the anvil reporter when anvilTag is set', async () => {
102
+ const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
103
+
104
+ getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
105
+
106
+ await action({ options: { anvilTag: 'platform' } })
107
+
108
+ expect(jestRunCLIMock).toHaveBeenCalledWith(
109
+ expect.objectContaining({
110
+ reporters: [
111
+ [
112
+ 'jest-silent-reporter',
113
+ {
114
+ useDots: true,
115
+ showPaths: true,
116
+ showWarnings: true,
117
+ },
118
+ ],
119
+ [
120
+ 'jest-junit',
121
+ {
122
+ outputDirectory: 'reports',
123
+ },
124
+ ],
125
+ [
126
+ 'jest-html-reporters',
127
+ {
128
+ publicPath: './reports',
129
+ },
130
+ ],
131
+ [
132
+ expect.stringContaining(
133
+ 'davinci/packages/qa/src/reporters/jest-anvil-reporter.js'
134
+ ),
135
+ {
136
+ anvilTag: 'platform',
137
+ },
138
+ ],
139
+ ],
140
+ }),
141
+ expect.anything()
142
+ )
143
+ })
144
+
145
+ it('allows overriding of the default Davinci reporters via custom configuration', async () => {
146
+ const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
147
+
148
+ getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
149
+ const customCLIReporter = 'My-CLI-Reporter'
150
+
151
+ await action({
152
+ options: {
153
+ anvilTag: 'MyTag',
154
+ reporters: customCLIReporter,
155
+ },
156
+ })
157
+
158
+ expect(jestRunCLIMock).toHaveBeenCalledWith(
159
+ expect.objectContaining({
160
+ reporters: [customCLIReporter],
161
+ }),
162
+ expect.anything()
163
+ )
164
+ })
165
+
166
+ it('allows overriding the default Davinci reporters via custom jest config', async () => {
167
+ const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
168
+
169
+ getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
170
+ const customJestConfigReporter = 'My-Jest-Config-Reporter'
171
+
172
+ getProjectRootFileContentMock.mockReturnValue({
173
+ reporters: customJestConfigReporter,
174
+ })
175
+
176
+ await action({ options: { config: {} } })
177
+
178
+ expect(jestRunCLIMock).toHaveBeenCalledWith(
179
+ expect.objectContaining({
180
+ reporters: [customJestConfigReporter],
181
+ }),
182
+ expect.anything()
183
+ )
184
+ })
185
+
186
+ it('prioritizes the CLI reporters over the custom jest config or the default Davinci reporters', async () => {
187
+ const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
188
+
189
+ getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
190
+ const customCLIReporter = 'My-CLI-Reporter'
191
+ const customJestConfigReporter = 'My-Jest-Config-Reporter'
192
+
193
+ getProjectRootFileContentMock.mockReturnValue({
194
+ reporters: customJestConfigReporter,
195
+ })
196
+
197
+ await action({
198
+ options: {
199
+ anvilTag: 'MyTag',
200
+ reporters: customCLIReporter,
201
+ config: {},
202
+ },
203
+ })
204
+
205
+ expect(jestRunCLIMock).toHaveBeenCalledWith(
206
+ expect.objectContaining({
207
+ reporters: [customCLIReporter],
208
+ }),
209
+ expect.anything()
210
+ )
211
+ })
212
+ })
213
+ })
@@ -0,0 +1,112 @@
1
+ const glob = require('glob')
2
+ const fs = require('fs')
3
+
4
+ const parallelization = require('./parallelization')
5
+
6
+ const mockedConfig = {
7
+ specPattern: '**.test.js',
8
+ }
9
+ const mockedTestFiles = [
10
+ 'test1.test.js',
11
+ 'test2.test.js',
12
+ 'test3.test.js',
13
+ 'test4.test.js',
14
+ ]
15
+ const mockedSizes = {
16
+ 'test1.test.js': 128,
17
+ 'test2.test.js': 256,
18
+ 'test3.test.js': 512,
19
+ 'test4.test.js': 1024,
20
+ }
21
+
22
+ jest.mock('test-project/test1.test.js', () => {}, {
23
+ virtual: true,
24
+ })
25
+ jest.mock('test-project/test2.test.js', () => {}, {
26
+ virtual: true,
27
+ })
28
+ jest.mock('test-project/test3.test.js', () => {}, {
29
+ virtual: true,
30
+ })
31
+ jest.mock('test-project/test4.test.js', () => {}, {
32
+ virtual: true,
33
+ })
34
+
35
+ describe('Cypress CI parallelization', () => {
36
+ const env = process.env
37
+ let config = mockedConfig
38
+ let globSyncMock
39
+
40
+ beforeEach(() => {
41
+ process.env = { ...env }
42
+ config = { ...mockedConfig }
43
+
44
+ globSyncMock = jest.spyOn(glob, 'sync')
45
+ jest.spyOn(fs, 'statSync').mockImplementation(fileName => ({
46
+ size: mockedSizes[fileName],
47
+ }))
48
+ })
49
+
50
+ afterEach(() => {
51
+ jest.restoreAllMocks()
52
+ })
53
+
54
+ describe('Sort files by size and split in two parallel groups', () => {
55
+ it('returns first half', () => {
56
+ process.env.GROUP_INDEX = 0
57
+ process.env.PARALLEL_GROUPS = 2
58
+
59
+ globSyncMock.mockImplementation(() => mockedTestFiles.slice(0))
60
+
61
+ parallelization(config)
62
+
63
+ expect(config.specPattern).toEqual(['test4.test.js', 'test2.test.js'])
64
+ })
65
+
66
+ it('returns second half', () => {
67
+ process.env.GROUP_INDEX = 1
68
+ process.env.PARALLEL_GROUPS = 2
69
+
70
+ globSyncMock.mockImplementation(() => mockedTestFiles.slice(0))
71
+
72
+ parallelization(config)
73
+
74
+ expect(config.specPattern).toEqual(['test3.test.js', 'test1.test.js'])
75
+ })
76
+ })
77
+
78
+ describe('When there is only 1 parallel group', () => {
79
+ it('returns spec pattern', () => {
80
+ process.env.GROUP_INDEX = 0
81
+ process.env.PARALLEL_GROUPS = 1
82
+
83
+ parallelization(config)
84
+
85
+ expect(config.specPattern).toBe('**.test.js')
86
+ })
87
+ })
88
+
89
+ describe('When group index is lower than 0', () => {
90
+ it('returns spec pattern', () => {
91
+ process.env.GROUP_INDEX = -1
92
+ process.env.PARALLEL_GROUPS = 3
93
+
94
+ parallelization(config)
95
+
96
+ expect(config.specPattern).toBe('**.test.js')
97
+ })
98
+ })
99
+
100
+ describe('When there are less test files than testing groups', () => {
101
+ it('returns empty array for a group', () => {
102
+ process.env.GROUP_INDEX = 1
103
+ process.env.PARALLEL_GROUPS = 2
104
+
105
+ globSyncMock.mockImplementation(() => mockedTestFiles.slice(0, 1))
106
+
107
+ parallelization(config)
108
+
109
+ expect(config.specPattern).toEqual([])
110
+ })
111
+ })
112
+ })