@toptal/davinci-qa 6.0.2 → 7.0.1-alpha-chore-add-parallelization-to-unit-tests.7

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,17 @@
1
1
  # Change Log
2
2
 
3
+ ## 7.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#1224](https://github.com/toptal/davinci/pull/1224) [`caf0ee02`](https://github.com/toptal/davinci/commit/caf0ee027107be2acf0315deed33ef89d34f6b69) Thanks [@rafael-anachoreta](https://github.com/rafael-anachoreta)! - ---
8
+
9
+ ### Anvil reporter
10
+
11
+ - Add Anvil test mapper
12
+ - Add Anvil reporter to Cypress
13
+ - Update Jest Anvil reporter to use new mapper
14
+
3
15
  ## 6.0.2
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toptal/davinci-qa",
3
- "version": "6.0.2",
3
+ "version": "7.0.1-alpha-chore-add-parallelization-to-unit-tests.7+62642e12",
4
4
  "description": "QA package to test your application",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -37,10 +37,11 @@
37
37
  "@cypress/webpack-preprocessor": "^5.7.0",
38
38
  "@testing-library/jest-dom": "^5.14.1",
39
39
  "@testing-library/react": "^12.1.2",
40
- "@toptal/davinci-cli-shared": "1.5.1",
40
+ "@toptal/davinci-cli-shared": "1.5.2-alpha-chore-add-parallelization-to-unit-tests.58+62642e12",
41
41
  "@types/jest": "^27.4.1",
42
42
  "babel-jest": "^27.5.1",
43
43
  "cypress": "^9.4.1",
44
+ "enhanced-resolve": "^5.9.2",
44
45
  "fs-extra": "^10.0.0",
45
46
  "glob": "^7.2.0",
46
47
  "jest": "^27.5.1",
@@ -50,7 +51,10 @@
50
51
  "jest-styled-components": "^7.0.8",
51
52
  "jsdom": "^19.0.0",
52
53
  "matchmedia-polyfill": "^0.3.2",
53
- "semver": "^7.3.2",
54
- "enhanced-resolve": "^5.9.2"
55
- }
54
+ "semver": "^7.3.2"
55
+ },
56
+ "devDependencies": {
57
+ "mocha": "^9.2.2"
58
+ },
59
+ "gitHead": "62642e124f62c12a3d5c6c967b7ad66e4836d8e1"
56
60
  }
@@ -6,11 +6,7 @@ const {
6
6
  } = require('@toptal/davinci-cli-shared')
7
7
 
8
8
  const endToEndTestCommand = ({
9
- options: {
10
- open = false,
11
- baseUrl,
12
- ...cypressOptions
13
- } = {},
9
+ options: { open = false, baseUrl, anvilTag, ...cypressOptions } = {},
14
10
  files: testFiles = []
15
11
  }) => {
16
12
  print.green('Running end to end tests...')
@@ -19,6 +15,10 @@ const endToEndTestCommand = ({
19
15
  '@toptal/davinci-qa',
20
16
  'src/configs/cypress.config.json'
21
17
  )
18
+ const cypressReporterPath = files.getPackageFilePath(
19
+ '@toptal/davinci-qa',
20
+ 'src/reporters/cypress-anvil-reporter.js'
21
+ )
22
22
 
23
23
  const mode = open ? 'open' : 'run'
24
24
 
@@ -29,6 +29,14 @@ const endToEndTestCommand = ({
29
29
  configPath,
30
30
  ...testFiles,
31
31
  ...(baseUrl ? ['--config', `baseUrl=${baseUrl}`] : []),
32
+ ...(!open && anvilTag
33
+ ? [
34
+ '--reporter',
35
+ cypressReporterPath,
36
+ '--reporter-options',
37
+ `anvilTag=${anvilTag}`
38
+ ]
39
+ : []),
32
40
  ...convertToCLIParameters(cypressOptions)
33
41
  ])
34
42
  }
@@ -45,8 +53,13 @@ const endToEndCommandCreator = {
45
53
  name: '--open'
46
54
  },
47
55
  {
48
- label: 'opens the Cypress Test Runner',
56
+ label: "URL used as prefix for cy.visit() or cy.request() command's URL",
49
57
  name: '--baseUrl <baseUrl>'
58
+ },
59
+ {
60
+ label:
61
+ "the project name matching Anvil's expected tag to generate Anvil Reports",
62
+ name: '--anvilTag <anvilTag>'
50
63
  }
51
64
  ]
52
65
  }
@@ -9,37 +9,44 @@ const getRootProjectPath = relativePath => path.join(__dirname, relativePath)
9
9
 
10
10
  const defaultCIJestReporters = anvilTag => [
11
11
  [
12
- 'jest-silent-reporter', {
13
- 'useDots': true,
14
- 'showPaths': true,
15
- 'showWarnings': true
12
+ 'jest-silent-reporter',
13
+ {
14
+ useDots: true,
15
+ showPaths: true,
16
+ showWarnings: true
16
17
  }
17
18
  ],
18
19
  [
19
- 'jest-junit', {
20
- 'outputDirectory': 'reports'
20
+ 'jest-junit',
21
+ {
22
+ outputDirectory: 'reports'
21
23
  }
22
24
  ],
23
25
  [
24
- 'jest-html-reporters', {
25
- 'publicPath': './reports',
26
+ 'jest-html-reporters',
27
+ {
28
+ publicPath: './reports'
26
29
  }
27
30
  ],
28
31
  [
29
- getRootProjectPath('../reporters/jest-anvil-reporter.js'), {
30
- 'anvilTag': anvilTag
32
+ getRootProjectPath('../reporters/jest-anvil-reporter.js'),
33
+ {
34
+ anvilTag: anvilTag
31
35
  }
32
36
  ]
33
37
  ]
34
38
 
35
39
  const unitTestsCommand = ({
36
- options: { inspect = false, anvilTag, ...jestOptions } = {},
40
+ options: { anvilTag, ...jestOptions } = {},
37
41
  files: testPathPattern = []
38
42
  }) => {
39
43
  print.green('Running unit tests...')
40
44
 
41
45
  const currentRunningDir = process.cwd()
42
- const jestConfigPath = files.getPackageFilePath('@toptal/davinci-qa', 'src/configs/jest.config.js')
46
+ const jestConfigPath = files.getPackageFilePath(
47
+ '@toptal/davinci-qa',
48
+ 'src/configs/jest.config.js'
49
+ )
43
50
  const packageJson = files.getProjectRootFileContent('./package.json')
44
51
 
45
52
  try {
@@ -56,8 +63,10 @@ const unitTestsCommand = ({
56
63
 
57
64
  if (isCi) {
58
65
  const hasCustomConfig = Boolean(jestOptions.config)
59
- const jestProjectConfig = hasCustomConfig && files.getProjectRootFileContent(jestOptions.config)
60
- const isConfigFileHasCollectCoverageFrom = hasCustomConfig && jestProjectConfig.collectCoverageFrom
66
+ const jestProjectConfig =
67
+ hasCustomConfig && files.getProjectRootFileContent(jestOptions.config)
68
+ const isConfigFileHasCollectCoverageFrom =
69
+ hasCustomConfig && jestProjectConfig.collectCoverageFrom
61
70
 
62
71
  defaultOptions.coverage = true
63
72
  defaultOptions.coverageDirectory = 'coverage'
@@ -75,13 +84,17 @@ const unitTestsCommand = ({
75
84
  // Override order: CLI options -> reporters on jest.config -> Davinci defaults
76
85
  if (jestOptions.reporters) {
77
86
  defaultOptions.reporters = jestOptions.reporters
78
- }
79
- else if (jestProjectConfig?.reporters) {
87
+ } else if (jestProjectConfig?.reporters) {
80
88
  defaultOptions.reporters = jestProjectConfig.reporters
81
89
  } else {
82
90
  defaultOptions.testLocationInResults = true // allows reporters to return test line number
83
91
  defaultOptions.reporters = defaultCIJestReporters(anvilTag)
84
92
  }
93
+
94
+ defaultOptions.maxWorkers = 1
95
+ defaultOptions.runInBand = false
96
+ defaultOptions.testSequencer =
97
+ './node_modules/@toptal/davinci-qa/src/configs/jest/parallel-ci-sequencer.js'
85
98
  }
86
99
 
87
100
  const options = {
@@ -91,14 +104,15 @@ const unitTestsCommand = ({
91
104
 
92
105
  print.grey('Jest arguments:', options)
93
106
 
94
- return jest.runCLI(options, [currentRunningDir])
95
- .then(jestResult => {
96
- if (jestResult.results.success) {
97
- return
98
- }
107
+ return jest.runCLI(options, [currentRunningDir]).then(jestResult => {
108
+ if (jestResult.results.success) {
109
+ return
110
+ }
99
111
 
100
- process.exit(1)
101
- })
112
+ process.exit(1)
113
+
114
+ return
115
+ })
102
116
  }
103
117
 
104
118
  const unitTestsCommandCreator = {
@@ -113,13 +127,8 @@ const unitTestsCommandCreator = {
113
127
  },
114
128
  {
115
129
  label:
116
- 'the project name matching Anvil\'s expected tag. Used to generate Anvil-compatible test reports.',
130
+ "the project name matching Anvil's expected tag. Used to generate Anvil-compatible test reports.",
117
131
  name: '--anvilTag <anvilTag>'
118
- },
119
- {
120
- label:
121
- 'start the test run in inspect mode, allowing dev tools to be attached to the test runner process. Read more at https://toptal-core.atlassian.net/wiki/spaces/FE/pages/1446379739/Profiling+Unit+Tests.',
122
- name: '--inspect'
123
132
  }
124
133
  ]
125
134
  }
@@ -0,0 +1,36 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
+ const Sequencer = require('@jest/test-sequencer').default
3
+
4
+ const sortByPath = (test1, test2) => {
5
+ return test1.path - test2.path
6
+ }
7
+
8
+ class ParallelCISequencer extends Sequencer {
9
+ constructor() {
10
+ super()
11
+ this.ciNodeIndex = Number(process.env.GROUP_INDEX || '1')
12
+ this.ciNodeTotal = Number(process.env.GROUP_TOTAL || '1')
13
+ }
14
+
15
+ sort(tests) {
16
+ const sortedTests = [...tests].sort(sortByPath)
17
+ const testsForThisRunner = this.distributeAcrossCINodes(sortedTests)
18
+
19
+ console.log(`CI_NODE_INDEX: ${this.ciNodeIndex}`)
20
+ console.log(`CI_NODE_TOTAL: ${this.ciNodeTotal}`)
21
+ console.log(`Total number of tests: ${tests.length}`)
22
+ console.log(
23
+ `Total number of tests for this runner: ${testsForThisRunner.length}`
24
+ )
25
+
26
+ return testsForThisRunner
27
+ }
28
+
29
+ distributeAcrossCINodes(tests) {
30
+ return tests.filter((test, index) => {
31
+ return index % this.ciNodeTotal === this.ciNodeIndex - 1
32
+ })
33
+ }
34
+ }
35
+
36
+ module.exports = ParallelCISequencer
@@ -0,0 +1,46 @@
1
+ const { print } = require('@toptal/davinci-cli-shared')
2
+ const fs = require('fs-extra')
3
+
4
+ const anvilMapper = ({
5
+ fileName,
6
+ lineNumber,
7
+ status,
8
+ durationInSeconds,
9
+ scenarioName,
10
+ description,
11
+ error,
12
+ backtrace,
13
+ testType,
14
+ pendingMessage,
15
+ tag
16
+ }) => ({
17
+ file_name: fileName,
18
+ line_number: lineNumber,
19
+ status: status === 'todo' ? 'pending' : status,
20
+ duration: durationInSeconds,
21
+ scenario_name: scenarioName,
22
+ description,
23
+ error,
24
+ backtrace,
25
+ test_type: testType,
26
+ pending_message: pendingMessage,
27
+ tag
28
+ })
29
+
30
+ const generateAnvilResults = (fileName, testResults) => {
31
+ const reportPath = './reports'
32
+
33
+ try {
34
+ fs.ensureDirSync(reportPath)
35
+ fs.writeJsonSync(`${reportPath}/${fileName}`, testResults)
36
+ print.green('[anvil-reporter] Anvil reports generated successfully!')
37
+ } catch (err) {
38
+ print.red('[anvil-reporter] Failed to create Anvil test results!', err)
39
+ throw err
40
+ }
41
+ }
42
+
43
+ module.exports = {
44
+ anvilMapper,
45
+ generateAnvilResults
46
+ }
@@ -0,0 +1,121 @@
1
+ const { print } = require('@toptal/davinci-cli-shared')
2
+ const { Runner, reporters } = require('mocha')
3
+
4
+ const { anvilMapper, generateAnvilResults } = require('./anvil-results-helper')
5
+
6
+ const { Spec, Base } = reporters
7
+ const {
8
+ EVENT_RUN_END,
9
+ EVENT_TEST_FAIL,
10
+ EVENT_TEST_PASS,
11
+ EVENT_TEST_PENDING,
12
+ EVENT_TEST_RETRY
13
+ } = Runner.constants
14
+
15
+ class CypressAnvilReporter extends Base {
16
+ constructor(runner, options) {
17
+ super(runner, options)
18
+ const tests = []
19
+ const joinParentTitles = obj =>
20
+ obj?.parent?.title
21
+ ? `${joinParentTitles(obj.parent)}/${obj.title}`
22
+ : obj.title
23
+
24
+ // Spec is Cypress's default reporter, but it misses retries
25
+ this._specReporter = new Spec(
26
+ runner.on(EVENT_TEST_RETRY, test => {
27
+ print.yellow(
28
+ `(Attempt ${test?.currentRetry + 1} of ${test?.retries}) ${
29
+ test?.title
30
+ }`
31
+ )
32
+ }),
33
+ options
34
+ )
35
+
36
+ const anvilTag = options?.reporterOptions?.anvilTag
37
+
38
+ if (!anvilTag) {
39
+ print.yellow(
40
+ "[anvil-reporter] --anvilTag option is missing! anvil_cypress_test_results.json won't be generated."
41
+ )
42
+ } else {
43
+ runner
44
+ .on(EVENT_TEST_PASS, test => {
45
+ tests.push(
46
+ anvilMapper({
47
+ fileName: test?.invocationDetails?.absoluteFile,
48
+ lineNumber: test?.invocationDetails?.line,
49
+ status: test?.state,
50
+ durationInSeconds: test?.duration / 1000,
51
+ scenarioName: joinParentTitles(test),
52
+ description: test?.title,
53
+ error: test?.err?.message || null,
54
+ backtrace: test?.err?.stack || null,
55
+ testType: 'integration',
56
+ pendingMessage: null,
57
+ tag: anvilTag
58
+ })
59
+ )
60
+ })
61
+ .on(EVENT_TEST_FAIL, test => {
62
+ tests.push(
63
+ anvilMapper({
64
+ fileName: test?.invocationDetails?.absoluteFile,
65
+ lineNumber: test?.invocationDetails?.line,
66
+ status: test?.state,
67
+ durationInSeconds: test?.duration / 1000,
68
+ scenarioName: joinParentTitles(test),
69
+ description: test?.title,
70
+ error: test?.err?.message || null,
71
+ backtrace: test?.err?.stack || null,
72
+ testType: 'integration',
73
+ pendingMessage: null,
74
+ tag: anvilTag
75
+ })
76
+ )
77
+ })
78
+ .on(EVENT_TEST_PENDING, test => {
79
+ tests.push(
80
+ anvilMapper({
81
+ fileName: test?.invocationDetails?.absoluteFile,
82
+ lineNumber: test?.invocationDetails?.line,
83
+ status: test?.state,
84
+ durationInSeconds: 0,
85
+ scenarioName: joinParentTitles(test),
86
+ description: test?.title,
87
+ error: test?.err?.message || null,
88
+ backtrace: test?.err?.stack || null,
89
+ testType: 'integration',
90
+ pendingMessage: null,
91
+ tag: anvilTag
92
+ })
93
+ )
94
+ })
95
+ .on(EVENT_TEST_RETRY, test => {
96
+ tests.push(
97
+ anvilMapper({
98
+ fileName: test?.invocationDetails?.absoluteFile,
99
+ lineNumber: test?.invocationDetails?.line,
100
+ status: 'retried',
101
+ durationInSeconds: test?.duration / 1000,
102
+ scenarioName: joinParentTitles(test),
103
+ description: test?.title,
104
+ error: test?.err?.message || null,
105
+ backtrace: test?.err?.stack || null,
106
+ testType: 'integration',
107
+ pendingMessage: null,
108
+ tag: anvilTag
109
+ })
110
+ )
111
+ })
112
+ .once(EVENT_RUN_END, () => {
113
+ generateAnvilResults('anvil_cypress_test_results.json', {
114
+ test_results: tests
115
+ })
116
+ })
117
+ }
118
+ }
119
+ }
120
+
121
+ module.exports = CypressAnvilReporter
@@ -1,52 +1,61 @@
1
1
  const { print } = require('@toptal/davinci-cli-shared')
2
- const fs = require('fs')
2
+
3
+ const { anvilMapper, generateAnvilResults } = require('./anvil-results-helper')
3
4
 
4
5
  class JestAnvilReporter {
5
- constructor(globalConfig, options) {
6
- this._globalConfig = globalConfig
7
- this._options = options
8
- }
6
+ constructor(globalConfig, options) {
7
+ this._globalConfig = globalConfig
8
+ this._options = options
9
+ }
10
+
11
+ onRunComplete(contexts, results) {
12
+ print.grey('Starting Anvil reporter processing...')
13
+ console.time('Anvil reporter run')
14
+ const fileTestResults = results.testResults
15
+ const runPath = process.cwd()
16
+ const individualTestResults = fileTestResults.flatMap(fileTestResult => {
17
+ const fileName = fileTestResult.testFilePath.replace(runPath, '.')
18
+
19
+ return fileTestResult.testResults.map(res => ({ ...res, fileName }))
20
+ })
9
21
 
10
- onRunComplete(contexts, results) {
11
- print.grey('Starting Anvil reporter processing...')
12
- console.time('Anvil reporter run')
13
- const fileTestResults = results.testResults
14
- const runPath = process.cwd()
15
- const individualTestResults = fileTestResults.flatMap(fileTestResult => {
16
- const fileName = fileTestResult.testFilePath.replace(runPath, '.')
17
- return fileTestResult.testResults.map(res => ({ ...res, fileName }))
18
- })
19
- const anvilTestResults = individualTestResults.map(testResult => {
20
- const anvilResults = {
21
- file_name: testResult.fileName,
22
- line_number: testResult.location && testResult.location.line,
23
- status: testResult.status === 'todo' ? 'pending' : testResult.status,
24
- duration: testResult.duration,
25
- scenario_name: testResult.ancestorTitles.concat(testResult.title).join('/'),
26
- description: testResult.title,
27
- error: testResult.failureDetails.length > 0 ? testResult.failureDetails[0].message : null,
28
- backtrace: testResult.failureDetails.length > 0 ? testResult.failureDetails.map(failure => failure.stack) : null,
29
- test_type: 'unit',
30
- pending_message: null
31
- }
32
- if (this._options && this._options.anvilTag) {
33
- anvilResults['tag'] = this._options.anvilTag
34
- }
35
- return anvilResults
36
- })
37
- const anvilPayload = {
38
- "test_results": [
39
- ...anvilTestResults
40
- ]
41
- }
42
- fs.writeFileSync('./anvil_test_results.json', JSON.stringify(anvilPayload, undefined, 2), err => {
43
- if (err) {
44
- print.red('Failed to create Anvil test results', err)
45
- throw err
46
- }
47
- })
48
- console.timeEnd('Anvil reporter run')
22
+ if (!this?._options?.anvilTag) {
23
+ print.yellow(
24
+ 'Missing --anvilTag value! Anvil test results will be skipped.'
25
+ )
26
+
27
+ return
28
+ }
29
+ const anvilTestResults = individualTestResults.map(testResult => {
30
+ return anvilMapper({
31
+ fileName: testResult.fileName,
32
+ lineNumber: testResult.location && testResult.location.line,
33
+ status: testResult.status,
34
+ durationInSeconds: testResult.duration / 1000,
35
+ scenarioName: testResult.ancestorTitles
36
+ .concat(testResult.title)
37
+ .join('/'),
38
+ description: testResult.title,
39
+ error:
40
+ testResult.failureDetails.length > 0
41
+ ? testResult.failureDetails[0].message
42
+ : null,
43
+ backtrace:
44
+ testResult.failureDetails.length > 0
45
+ ? testResult.failureDetails.map(failure => failure.stack)
46
+ : null,
47
+ testType: 'unit',
48
+ pendingMessage: null,
49
+ tag: this._options.anvilTag
50
+ })
51
+ })
52
+ const anvilPayload = {
53
+ test_results: [...anvilTestResults]
49
54
  }
55
+
56
+ generateAnvilResults('anvil_jest_test_results.json', anvilPayload)
57
+ console.timeEnd('Anvil reporter run')
58
+ }
50
59
  }
51
-
60
+
52
61
  module.exports = JestAnvilReporter
@@ -1,56 +0,0 @@
1
- {
2
- "name": "@toptal/davinci-qa",
3
- "version": "6.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.14.8",
33
- "@babel/preset-env": "^7.16.4",
34
- "@babel/preset-react": "^7.10.4",
35
- "@babel/preset-typescript": "^7.10.4",
36
- "@cypress/code-coverage": "^3.9.12",
37
- "@cypress/webpack-preprocessor": "^5.7.0",
38
- "@testing-library/jest-dom": "^5.14.1",
39
- "@testing-library/react": "^12.1.2",
40
- "@toptal/davinci-cli-shared": "1.5.1",
41
- "@types/jest": "^27.4.1",
42
- "babel-jest": "^27.5.1",
43
- "cypress": "^9.4.1",
44
- "fs-extra": "^10.0.0",
45
- "glob": "^7.2.0",
46
- "jest": "^27.5.1",
47
- "jest-html-reporters": "^3.0.6",
48
- "jest-junit": "^13.1.0",
49
- "jest-silent-reporter": "^0.5.0",
50
- "jest-styled-components": "^7.0.8",
51
- "jsdom": "^19.0.0",
52
- "matchmedia-polyfill": "^0.3.2",
53
- "semver": "^7.3.2",
54
- "enhanced-resolve": "^5.9.2"
55
- }
56
- }
@@ -1,65 +0,0 @@
1
- const convertToCLIParametersMock = jest.fn()
2
- const getPackageFilePathMock = jest.fn()
3
-
4
- jest.mock('@toptal/davinci-cli-shared', () => ({
5
- runSync: jest.fn(),
6
- print: {
7
- green: jest.fn()
8
- },
9
- convertToCLIParameters: convertToCLIParametersMock,
10
- files: {
11
- getPackageFilePath: getPackageFilePathMock
12
- }
13
- }))
14
-
15
- const { runSync } = require('@toptal/davinci-cli-shared')
16
-
17
- const { action } = require('./end-to-end-tests')
18
-
19
- describe('endToEndTestCommand', () => {
20
- it('runs cypress with default options', () => {
21
- convertToCLIParametersMock.mockReturnValueOnce([])
22
- getPackageFilePathMock.mockReturnValueOnce('packages/qa/src/configs/cypress.config.json')
23
-
24
- action({ options: {} })
25
-
26
- expect(runSync).toHaveBeenCalledWith('yarn', [
27
- 'cypress',
28
- 'run',
29
- '--config-file',
30
- 'packages/qa/src/configs/cypress.config.json'
31
- ])
32
-
33
- expect(convertToCLIParametersMock).toHaveBeenCalledWith({})
34
- expect(getPackageFilePathMock).toHaveBeenCalledWith(
35
- '@toptal/davinci-qa',
36
- 'src/configs/cypress.config.json'
37
- )
38
- })
39
-
40
- it('runs cypress with custom options', () => {
41
- const spec = 'cypress/integration/activation_flow.spec.ts'
42
-
43
- convertToCLIParametersMock.mockReturnValueOnce(['--spec', spec])
44
- getPackageFilePathMock.mockReturnValueOnce('packages/qa/src/configs/cypress.config.json')
45
-
46
- action({ options: { spec } })
47
-
48
- expect(runSync).toHaveBeenCalledWith('yarn', [
49
- 'cypress',
50
- 'run',
51
- '--config-file',
52
- 'packages/qa/src/configs/cypress.config.json',
53
- '--spec',
54
- spec
55
- ])
56
-
57
- expect(convertToCLIParametersMock).toHaveBeenCalledWith({
58
- spec
59
- })
60
- expect(getPackageFilePathMock).toHaveBeenCalledWith(
61
- '@toptal/davinci-qa',
62
- 'src/configs/cypress.config.json'
63
- )
64
- })
65
- })
@@ -1,164 +0,0 @@
1
- const jestRunCLIMock =jest.fn().mockResolvedValue({ results: {success: true}})
2
- const getPackageFilePathMock = jest.fn()
3
- const getProjectRootFilePathMock = jest.fn()
4
- const getProjectRootFileContentMock = jest.fn().mockReturnValue({
5
- devDependencies: {}, dependencies: {},
6
- })
7
-
8
- jest.mock('@toptal/davinci-cli-shared', () => ({
9
- runSync: jest.fn(),
10
- print: {
11
- green: jest.fn(),
12
- grey: jest.fn(),
13
- red: jest.fn(),
14
- },
15
- files: {
16
- getPackageFilePath: getPackageFilePathMock,
17
- getProjectRootFileContent: getProjectRootFileContentMock,
18
- getProjectRootFilePath: getProjectRootFilePathMock
19
- }
20
- }))
21
-
22
- jest.mock('jest', () => ({
23
- runCLI: jestRunCLIMock
24
- }))
25
-
26
- const { action } = require('./unit-tests')
27
- const originalEnv = process.env
28
-
29
- beforeEach(() => {
30
- jest.clearAllMocks()
31
- })
32
-
33
- afterEach(() => {
34
- process.env = originalEnv
35
- })
36
-
37
- describe('unitTestsCommand', () => {
38
- describe('when running locally', () => {
39
- beforeEach(() => {
40
- delete process.env.CI
41
- })
42
-
43
- it('calls the jest CLI without additional reporters', async () => {
44
- const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
45
- getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
46
-
47
- await action({})
48
-
49
- expect(jestRunCLIMock).toBeCalledWith(
50
- {"config": davinciJestConfigPath, "testPathPattern": []},
51
- expect.anything()
52
- )
53
- })
54
-
55
- })
56
-
57
- describe('when running on CI', () => {
58
- beforeEach(() => {
59
- process.env.CI = true
60
- })
61
-
62
- it('calls the jest CLI with default Davinci reporters', async () => {
63
- const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
64
- getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
65
-
66
- await action({})
67
-
68
- expect(jestRunCLIMock).toBeCalledWith(
69
- expect.objectContaining({
70
- reporters: [
71
- [
72
- "jest-silent-reporter",
73
- {
74
- useDots: true,
75
- showPaths: true,
76
- showWarnings: true,
77
- },
78
- ],
79
- [
80
- "jest-junit",
81
- {
82
- outputDirectory: "reports",
83
- },
84
- ],
85
- [
86
- "jest-html-reporters",
87
- {
88
- publicPath: "./reports",
89
- },
90
- ],
91
- [
92
- expect.stringContaining("davinci/packages/qa/src/reporters/jest-anvil-reporter.js"),
93
- {
94
- anvilTag: undefined,
95
- },
96
- ],
97
- ],
98
- }),
99
- expect.anything()
100
- )
101
- })
102
-
103
- it('allows overriding of the default Davinci reporters via custom configuration', async () => {
104
- const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
105
- getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
106
- const customCLIReporter = 'My-CLI-Reporter'
107
-
108
- await action({
109
- options: {
110
- anvilTag: 'MyTag',
111
- reporters: customCLIReporter
112
- }
113
- })
114
-
115
- expect(jestRunCLIMock).toBeCalledWith(
116
- expect.objectContaining({
117
- reporters: [customCLIReporter],
118
- }),
119
- expect.anything()
120
- )
121
- })
122
-
123
- it('allows overriding the default Davinci reporters via custom jest config', async () => {
124
- const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
125
- getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
126
- const customJestConfigReporter = 'My-Jest-Config-Reporter'
127
-
128
- getProjectRootFileContentMock.mockReturnValue({reporters: customJestConfigReporter})
129
-
130
- await action({ options: { config: {}}})
131
-
132
- expect(jestRunCLIMock).toBeCalledWith(
133
- expect.objectContaining({
134
- reporters: [customJestConfigReporter],
135
- }),
136
- expect.anything()
137
- )
138
- })
139
-
140
- it('prioritizes the CLI reporters over the custom jest config or the default Davinci reporters', async () => {
141
- const davinciJestConfigPath = 'packages/qa/src/configs/jest.config.js'
142
- getPackageFilePathMock.mockReturnValueOnce(davinciJestConfigPath)
143
- const customCLIReporter = 'My-CLI-Reporter'
144
- const customJestConfigReporter = 'My-Jest-Config-Reporter'
145
-
146
- getProjectRootFileContentMock.mockReturnValue({ reporters: customJestConfigReporter })
147
-
148
- await action({
149
- options: {
150
- anvilTag: 'MyTag',
151
- reporters: customCLIReporter,
152
- config: {}
153
- }
154
- })
155
-
156
- expect(jestRunCLIMock).toBeCalledWith(
157
- expect.objectContaining({
158
- reporters: [customCLIReporter],
159
- }),
160
- expect.anything()
161
- )
162
- })
163
- })
164
- })
@@ -1,145 +0,0 @@
1
- const JestAnvilReporter = require('./jest-anvil-reporter.js')
2
- const fs = require('fs')
3
- const {
4
- passedTestResults,
5
- failedTestResults,
6
- skippedTestResults,
7
- combinedTestResults,
8
- toDoTestResults
9
- } = require('./test-result-mocks')
10
-
11
- describe('Jest Anvil reporter', () => {
12
- const expectedPassedTestResults = {
13
- test_results: [
14
- {
15
- file_name: "/davinci/packages/qa/src/reporters/jest-anvil-reporter-mocker.test.js",
16
- line_number: 2,
17
- status: "passed",
18
- duration: 3,
19
- scenario_name: "Example test suite/1 + 1 returns 2",
20
- description: "1 + 1 returns 2",
21
- error: null,
22
- backtrace: null,
23
- test_type: "unit",
24
- pending_message: null,
25
- tag: "platform"
26
- }
27
- ]
28
- }
29
-
30
- const expectedFailedTestResults = {
31
- test_results: [
32
- {
33
- file_name: "/davinci/packages/qa/src/reporters/jest-anvil-reporter-mocker.test.js",
34
- line_number: 2,
35
- status: "failed",
36
- duration: 6,
37
- scenario_name: "Example test suite/1 + 1 returns 3",
38
- description: "1 + 1 returns 3",
39
- error: "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m3\u001b[39m\nReceived: \u001b[31m2\u001b[39m",
40
- backtrace: ["Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m3\u001b[39m\nReceived: \u001b[31m2\u001b[39m\n at Object.<anonymous> (/davinci/packages/qa/src/reporters/jest-anvil-reporter-mocker.test.js:3:17)\n at Object.asyncJestTest (/davinci/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)\n at /davinci/node_modules/jest-jasmine2/build/queueRunner.js:45:12\n at new Promise (<anonymous>)\n at mapper (/davinci/node_modules/jest-jasmine2/build/queueRunner.js:28:19)\n at /davinci/node_modules/jest-jasmine2/build/queueRunner.js:75:41"],
41
- test_type: "unit",
42
- pending_message: null,
43
- tag: "platform"
44
- }
45
- ]
46
- }
47
-
48
- const expectedSkippedTestResults = {
49
- test_results: [
50
- {
51
- file_name: "/davinci/packages/qa/src/reporters/jest-anvil-reporter-mocker.test.js",
52
- line_number: 2,
53
- status: "pending",
54
- duration: 0,
55
- scenario_name: "Example test suite/1 + 1 returns 2",
56
- description: "1 + 1 returns 2",
57
- error: null,
58
- backtrace: null,
59
- test_type: "unit",
60
- pending_message: null,
61
- tag: "platform"
62
- }
63
- ]
64
- }
65
-
66
- const expectedToDoTestResults = {
67
- test_results: [
68
- {
69
- file_name: "/davinci/packages/qa/src/reporters/jest-anvil-reporter-mocker.test.js",
70
- line_number: null,
71
- status: "pending",
72
- duration: 0,
73
- scenario_name: "Example test suite/1+1 returns 2",
74
- description: "1+1 returns 2",
75
- error: null,
76
- backtrace: null,
77
- test_type: "unit",
78
- pending_message: null,
79
- tag: "platform"
80
- }
81
- ]
82
- }
83
-
84
- const expectedCombinedTestResults = {
85
- test_results: [
86
- {
87
- file_name: "/davinci/packages/qa/src/reporters/jest-anvil-reporter-mocker.test.js",
88
- line_number: 2,
89
- status: "passed",
90
- duration: 5,
91
- scenario_name: "Example test suite/1 + 1 returns 2 (passed)",
92
- description: "1 + 1 returns 2 (passed)",
93
- error: null,
94
- backtrace: null,
95
- test_type: "unit",
96
- pending_message: null,
97
- tag: "platform"
98
- },
99
- {
100
- file_name: "/davinci/packages/qa/src/reporters/jest-anvil-reporter-mocker.test.js",
101
- line_number: 6,
102
- status: "failed",
103
- duration: 7,
104
- scenario_name: "Example test suite/1 + 1 returns 3 (failed)",
105
- description: "1 + 1 returns 3 (failed)",
106
- error: "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m3\u001b[39m\nReceived: \u001b[31m2\u001b[39m",
107
- backtrace: ["Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m3\u001b[39m\nReceived: \u001b[31m2\u001b[39m\n at Object.<anonymous> (/davinci/packages/qa/src/reporters/jest-anvil-reporter-mocker.test.js:7:17)\n at Object.asyncJestTest (/davinci/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)\n at /davinci/node_modules/jest-jasmine2/build/queueRunner.js:45:12\n at new Promise (<anonymous>)\n at mapper (/davinci/node_modules/jest-jasmine2/build/queueRunner.js:28:19)\n at /davinci/node_modules/jest-jasmine2/build/queueRunner.js:75:41"],
108
- test_type: "unit",
109
- pending_message: null,
110
- tag: "platform"
111
- },
112
- {
113
- file_name: "/davinci/packages/qa/src/reporters/jest-anvil-reporter-mocker.test.js",
114
- line_number: 10,
115
- status: "pending",
116
- duration: 0,
117
- scenario_name: "Example test suite/1 + 1 returns 2 (skipped)",
118
- description: "1 + 1 returns 2 (skipped)",
119
- error: null,
120
- backtrace: null,
121
- test_type: "unit",
122
- pending_message: null,
123
- tag: "platform"
124
- }
125
- ]
126
- }
127
-
128
- beforeEach(() => {
129
- jest.clearAllMocks();
130
- });
131
-
132
- test.each`
133
- input | expectedOutput | testType
134
- ${passedTestResults} | ${expectedPassedTestResults} | ${"passing tests"}
135
- ${failedTestResults} | ${expectedFailedTestResults} | ${"failing tests"}
136
- ${skippedTestResults} | ${expectedSkippedTestResults} | ${"skipped tests"}
137
- ${toDoTestResults} | ${expectedToDoTestResults} | ${"todo tests"}
138
- ${combinedTestResults} | ${expectedCombinedTestResults} | ${"a combination of test results"}
139
- `('returns a payload matching Anvil for $testType', ({ input, expectedOutput, testType }) => {
140
- const spy = jest.spyOn(fs, 'writeFileSync')
141
- const jestAnvilReporter = new JestAnvilReporter(undefined, { anvilTag: 'platform' })
142
- jestAnvilReporter.onRunComplete(undefined, input)
143
- expect(spy).toBeCalledWith("./anvil_test_results.json", JSON.stringify(expectedOutput, undefined, 2), expect.anything())
144
- })
145
- })
@@ -1,59 +0,0 @@
1
- const getPackageFileContentMock = jest.fn()
2
-
3
- jest.mock('@toptal/davinci-cli-shared', () => ({
4
- files: {
5
- getPackageFileContent: getPackageFileContentMock
6
- }
7
- }))
8
-
9
- const jestArgsToCLIRules = require('./jest-args-to-cli-converters')
10
-
11
- describe('jestArgsToCLIRules "reporters"', () => {
12
- const reportersRule = jestArgsToCLIRules['reporters']
13
-
14
- it('converts correctly arrays', () => {
15
- expect(
16
- reportersRule(['reporter1', 'reporter2'])
17
- ).toEqual(['reporter1', 'reporter2'])
18
- })
19
-
20
- it('converts correctly string', () => {
21
- expect(
22
- reportersRule('reporter1')
23
- ).toEqual(['reporter1'])
24
- })
25
- })
26
-
27
- describe('jestArgsToCLIRules "setupFilesAfterEnv"', () => {
28
- const setupFilesAfterEnvRule = jestArgsToCLIRules['setupFilesAfterEnv']
29
-
30
- it('takes default values from the davinci jest.config.js file', () => {
31
- getPackageFileContentMock.mockReturnValueOnce({
32
- setupFilesAfterEnv: ['davinci-setup-files']
33
- })
34
-
35
- expect(
36
- setupFilesAfterEnvRule()
37
- ).toEqual(['davinci-setup-files'])
38
- })
39
-
40
- it('merges default values with passed setup files', () => {
41
- getPackageFileContentMock.mockReturnValueOnce({
42
- setupFilesAfterEnv: ['davinci-setup-files']
43
- })
44
-
45
- expect(
46
- setupFilesAfterEnvRule(['file1', 'file2'])
47
- ).toEqual(['davinci-setup-files', 'file1', 'file2'])
48
- })
49
-
50
- it('merges default values with single passed setup file', () => {
51
- getPackageFileContentMock.mockReturnValueOnce({
52
- setupFilesAfterEnv: ['davinci-setup-files']
53
- })
54
-
55
- expect(
56
- setupFilesAfterEnvRule('file1')
57
- ).toEqual(['davinci-setup-files', 'file1'])
58
- })
59
- })
@@ -1,78 +0,0 @@
1
- const styledComponentsVersionCheck = require('./styled-components-version-check')
2
-
3
- const successTest = (styledComponentsVersion, dependenciesKeys) => {
4
- const packageJson = {}
5
-
6
- dependenciesKeys.forEach(dependenciesKey => {
7
- packageJson[dependenciesKey] = {
8
- 'styled-components': styledComponentsVersion
9
- }
10
- })
11
-
12
- let error = null
13
- try {
14
- styledComponentsVersionCheck(packageJson)
15
- } catch (e) {
16
- error = e
17
- }
18
-
19
- expect(error).toBeNull()
20
- }
21
-
22
- const errorTest = (styledComponentsVersion, dependenciesKeys) => {
23
- const packageJson = {}
24
-
25
- dependenciesKeys.forEach(dependenciesKey => {
26
- packageJson[dependenciesKey] = {
27
- 'styled-components': styledComponentsVersion
28
- }
29
- })
30
-
31
- let error = null
32
- try {
33
- styledComponentsVersionCheck(packageJson)
34
- } catch (e) {
35
- error = e
36
- }
37
-
38
- expect(error).not.toBeNull()
39
- expect(error.message).toContain(styledComponentsVersion)
40
- }
41
-
42
- describe('styled-components version check', () => {
43
- describe('has wrong (^4.4.1) SC version in dependencies', () => {
44
- test('should throw an error', () => {
45
- errorTest('^4.4.1', ['dependencies'])
46
- })
47
- })
48
-
49
- describe('has correct (^5.0.1) SC version in dependencies', () => {
50
- test('should NOT throw an error', () => {
51
- successTest('^5.0.1', ['dependencies'])
52
- })
53
- })
54
-
55
- describe('has wrong (^4.4.1) SC version in devDependencies and peerDependencies', () => {
56
- test('should throw an error', () => {
57
- errorTest('^4.4.1', ['devDependencies', 'peerDependencies'])
58
- })
59
- })
60
-
61
- describe('has correct (^5.0.1) SC version in devDependencies and peerDependencies', () => {
62
- test('should NOT throw an error', () => {
63
- successTest('^5.0.1', ['devDependencies', 'peerDependencies'])
64
- })
65
- })
66
-
67
- describe('has wrong (^4.4.1) SC version in devDependencies', () => {
68
- test('should throw an error', () => {
69
- errorTest('^4.4.1', ['devDependencies'])
70
- })
71
- })
72
-
73
- describe('has correct (^5.0.1) SC version in devDependencies', () => {
74
- test('should NOT throw an error', () => {
75
- successTest('^5.0.1', ['devDependencies'])
76
- })
77
- })
78
- })
@@ -1,32 +0,0 @@
1
- const toJestCLIArguments = require('./to-jest-cli-arguments')
2
-
3
- const EXAMPLE_REPORTER = 'some-jest-reporter'
4
- const ARGS_TO_CLI_RESULT = [EXAMPLE_REPORTER]
5
-
6
- jest.mock('./jest-args-to-cli-converters.js', () => ({
7
- 'reporters': () => ARGS_TO_CLI_RESULT
8
- }))
9
-
10
- describe('toJestCLIArguments', () => {
11
- it('converts boolean strings to booleans correctly', () => {
12
- expect(
13
- toJestCLIArguments({
14
- cache: 'false',
15
- ci: 'true'
16
- })
17
- ).toEqual({
18
- cache: false,
19
- ci: true
20
- })
21
- })
22
-
23
- it('converts some rules by using args-to-cli rules', () => {
24
- expect(
25
- toJestCLIArguments({
26
- 'reporters': EXAMPLE_REPORTER
27
- })
28
- ).toEqual({
29
- 'reporters': ARGS_TO_CLI_RESULT
30
- })
31
- })
32
- })