bahmutov / cypress-split

Split Cypress specs across parallel CI machines for speed
MIT License
208 stars 24 forks source link

Make cypress-split-preview return a json with filtered out specs with their updated timings that were run on the agent/machine #310

Closed rexizam closed 1 month ago

rexizam commented 1 month ago

I was wondering if it is possible to have that feature extended so that we can run:

npx cypress-split-preview --split 5 --splitIndex=3 --split-file timings.json

and the output would be:

{
  "durations": [
    {
      "spec": "cypress/integration/activeProcesses.integration-test.ts",
      "duration": 19389
    },
    {
      "spec": "cypress/integration/activityTracking/browsePerspective.integration-test.ts",
      "duration": 28346
    },
    {
      "spec": "cypress/integration/activityTracking/tasksPerspective.integration-test.ts",
      "duration": 13611
    }
  ]
}

In my use case I have created a micro service which posts the updated timings file but since I run 5 different agents my timings end up overwritten by the other agents.

I would be happy to get only the spec paths without the duration since I only need to send the specs that were run on the specific agent instead of sending all timings.

Also for some reason in preview chunk 1 and 2 are swapped around.

rexizam commented 1 month ago

I missed out on the documentation about specifying an output file in which cypress-split writes only the specs run on the agent. When I run that locally it's all good but When I run it in Jenkins it still writes all tests into the output file, not just the ones run on the agent. I also give the output files unique names.

rexizam commented 1 month ago

I had to write a shell script in Jenkins to post only the changed specs to my micro service. This is how I resolved my problem:

def updateCypressTimings(runnerId) {
  try {
    sh "jq --color-output . apps/instrument-ui/cypress-runner-${runnerId}.json"

    /* Read and parse the JSON files */
    def timingsFile = sh(script: """jq . apps/instrument-ui/cypress-timings.json""", returnStdout: true).trim()
    def runnerFile = sh(script: """jq . apps/instrument-ui/cypress-runner-${runnerId}.json""", returnStdout: true).trim()

    def timingsJson = new groovy.json.JsonSlurperClassic().parseText(timingsFile)
    def runnerJson = new groovy.json.JsonSlurperClassic().parseText(runnerFile)

    /* Compare durations and find changed specs */
    def changedSpecs = []

    runnerJson.durations.each { runnerSpec ->
      def timingsSpec = timingsJson.durations.find { it.spec == runnerSpec.spec }
      if (timingsSpec && timingsSpec.duration != runnerSpec.duration) {
          changedSpecs << runnerSpec
      }
    }

    /* Convert changed specs to JSON */
    def resultJson = '{"durations":[' + changedSpecs.collect {
      '{"spec":"' + it.spec + '","duration":' + it.duration + '}'
    }.join(",") + ']}'

    /* Write the result to a file */
    writeFile file: "apps/instrument-ui/specs.json", text: resultJson
    sh "jq --color-output . apps/instrument-ui/specs.json"

    sh '''
      # Extract and format the JSON data using jq
      json_data=$(jq . apps/instrument-ui/specs.json)
      # Post the JSON data using curl
      curl -X POST \
      -H "Content-Type: application/json" \
      -d "$json_data" \
      https://data.mongodb-api.com/app/instrument-cypress/endpoint/updateTimings
    '''
  } catch(Exception error) {
    String message = "An error occurred while updating cypress timings on MongoDB: \n\n ${error} \n\n ${env.BUILD_URL} \n\n ${env.JOB_URL}"
    echo "${error}"
  }
}