Repository: saucelabs/node-saucelabs
Pull request #233 (2024-01-09 18:35:15)

feat: Add test-runs API
Add test-runs post API to `saucelabs` package.


Patch:
diff --git a/docs/interface.md b/docs/interface.md
index cae12c9..eb96d76 100644
--- a/docs/interface.md
+++ b/docs/interface.md
@@ -910,4 +910,12 @@ This endpoint requires API Key authentication and will also return your private
         <br><h4>Options</h4>
         <ul>          <li><b>user_id</b>: user_id</li>          <li><b>org_id</b>: org_id</li>          <li><b>group_id</b>: group_id</li>          <li><b>team_id</b>: team_id</li>          <li><b>modified_since</b>: modified_since</li>          <li><b>completed</b>: completed</li>          <li><b>errored</b>: errored</li>          <li><b>failed</b>: failed</li>          <li><b>finished</b>: finished</li>          <li><b>new</b>: new</li>          <li><b>passed</b>: passed</li>          <li><b>public</b>: public</li>          <li><b>queued</b>: queued</li>          <li><b>running</b>: running</li>          <li><b>faulty</b>: faulty</li>          <li><b>limit</b>: Number of results to return</li>          <li><b>offset</b>: Starting number</li>        </ul>      </td>
     </tr>
+    <tr>
+      <td>
+        <b>POST</b> <code>/v1/</code><br>
+        Post Test Runs
+        <h3>Example:</h3>
+        <code>api.createTestRunsV1(parameters)</code>
+      </td>
+    </tr>
   </tbody></table>
diff --git a/package-lock.json b/package-lock.json
index 09d5547..376341e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3305,9 +3305,9 @@
       }
     },
     "node_modules/@types/http-cache-semantics": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
-      "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+      "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA=="
     },
     "node_modules/@types/istanbul-lib-coverage": {
       "version": "2.0.5",
@@ -3353,9 +3353,9 @@
       "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
     },
     "node_modules/@types/responselike": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
-      "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+      "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
       "dependencies": {
         "@types/node": "*"
       }
@@ -4319,12 +4319,12 @@
       }
     },
     "node_modules/cacheable-request": {
-      "version": "10.2.13",
-      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.13.tgz",
-      "integrity": "sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA==",
+      "version": "10.2.14",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz",
+      "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==",
       "dev": true,
       "dependencies": {
-        "@types/http-cache-semantics": "^4.0.1",
+        "@types/http-cache-semantics": "^4.0.2",
         "get-stream": "^6.0.1",
         "http-cache-semantics": "^4.1.1",
         "keyv": "^4.5.3",
@@ -7317,9 +7317,9 @@
       }
     },
     "node_modules/http2-wrapper": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz",
-      "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz",
+      "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
       "dev": true,
       "dependencies": {
         "quick-lru": "^5.1.1",
@@ -17427,9 +17427,9 @@
       }
     },
     "@types/http-cache-semantics": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
-      "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+      "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA=="
     },
     "@types/istanbul-lib-coverage": {
       "version": "2.0.5",
@@ -17475,9 +17475,9 @@
       "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
     },
     "@types/responselike": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
-      "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+      "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
       "requires": {
         "@types/node": "*"
       }
@@ -18159,12 +18159,12 @@
       "dev": true
     },
     "cacheable-request": {
-      "version": "10.2.13",
-      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.13.tgz",
-      "integrity": "sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA==",
+      "version": "10.2.14",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz",
+      "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==",
       "dev": true,
       "requires": {
-        "@types/http-cache-semantics": "^4.0.1",
+        "@types/http-cache-semantics": "^4.0.2",
         "get-stream": "^6.0.1",
         "http-cache-semantics": "^4.1.1",
         "keyv": "^4.5.3",
@@ -20413,9 +20413,9 @@
       }
     },
     "http2-wrapper": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz",
-      "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz",
+      "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
       "dev": true,
       "requires": {
         "quick-lru": "^5.1.1",
diff --git a/src/constants.js b/src/constants.js
index 7018eed..c74e523 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -33,6 +33,7 @@ const protocols = [
   require('../apis/autonomiq.json'),
   require('../apis/teamManagement.json'),
   require('../apis/builds.json'),
+  require('../apis/testruns.json'),
 ];
 
 const protocolFlattened = new Map();


Test patch:
diff --git a/apis/testruns.json b/apis/testruns.json
new file mode 100644
index 0000000..aa983d9
--- /dev/null
+++ b/apis/testruns.json
@@ -0,0 +1,357 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "title": "test-runs-api",
+    "version": "1.0.1"
+  },
+  "basePath": "/test-runs",
+  "servers": [
+    {
+      "url": "https://api.{region}.saucelabs.{tld}",
+      "variables": {
+        "region": {
+          "default": "us-west-1",
+          "description": "region of datacenter",
+          "enum": ["us-west-1", "eu-central-1", "us-east-4", "staging"]
+        },
+        "tld": {
+          "default": "com",
+          "description": "internal or external API",
+          "enum": ["net", "com"]
+        }
+      }
+    }
+  ],
+  "paths": {
+    "/v1/": {
+      "post": {
+        "summary": "Post Test Runs",
+        "operationId": "createTestRunsV1",
+        "parameters": [
+          {
+            "in": "body",
+            "name": "parameters",
+            "required": true,
+            "description": "create test runs request body",
+            "schema": {
+              "$ref": "#/definitions/TestRuns"
+            }
+          }
+        ],
+        "responses": {
+          "204": {
+            "description": "Test runs successfully created with no content returned"
+          },
+          "422": {
+            "description": "Validation Error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/definitions/HTTPValidationError"
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  },
+  "definitions": {
+    "ApiTesting": {
+      "title": "ApiTesting",
+      "type": "object",
+      "properties": {
+        "run_id": {
+          "title": "Run Id",
+          "type": "string"
+        },
+        "run_name": {
+          "title": "Run Name",
+          "type": "string"
+        },
+        "run_mode": {
+          "title": "Run Mode",
+          "type": "string"
+        },
+        "project_id": {
+          "title": "Project Id",
+          "type": "string"
+        },
+        "project_name": {
+          "title": "Project Name",
+          "type": "string"
+        },
+        "failures_count": {
+          "title": "Failures Count",
+          "type": "integer"
+        },
+        "warning_count": {
+          "title": "Warning Count",
+          "type": "integer"
+        }
+      }
+    },
+    "Ci": {
+      "title": "Ci",
+      "type": "object",
+      "properties": {
+        "ref_name": {
+          "title": "Ref Name",
+          "type": "string"
+        },
+        "commit_sha": {
+          "title": "Commit Sha",
+          "type": "string"
+        },
+        "repository": {
+          "title": "Repository",
+          "type": "string"
+        },
+        "branch": {
+          "title": "Branch",
+          "type": "string"
+        }
+      }
+    },
+    "HTTPValidationError": {
+      "title": "HTTPValidationError",
+      "type": "object",
+      "properties": {
+        "detail": {
+          "title": "Detail",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/ValidationError"
+          }
+        }
+      }
+    },
+    "TestRunPlatform": {
+      "title": "TestRunPlatform",
+      "enum": ["vdc", "rdc", "api", "other"],
+      "type": "string",
+      "description": "An enumeration."
+    },
+    "SauceJob": {
+      "title": "SauceJob",
+      "type": "object",
+      "properties": {
+        "id": {
+          "title": "Id",
+          "type": "string"
+        },
+        "name": {
+          "title": "Name",
+          "type": "string"
+        }
+      }
+    },
+    "Status": {
+      "title": "Status",
+      "enum": ["passed", "failed", "skipped"],
+      "type": "string",
+      "description": "An enumeration."
+    },
+    "TestRunTestError": {
+      "title": "TestRunTestError",
+      "type": "object",
+      "properties": {
+        "message": {
+          "title": "Message",
+          "type": "string"
+        },
+        "path": {
+          "title": "Path",
+          "type": "string"
+        },
+        "line": {
+          "title": "Line",
+          "type": "integer"
+        }
+      }
+    },
+    "TestRun": {
+      "title": "TestRun",
+      "required": ["name", "start_time", "end_time", "duration"],
+      "type": "object",
+      "properties": {
+        "name": {
+          "title": "Name",
+          "type": "string"
+        },
+        "id": {
+          "title": "Id",
+          "type": "string"
+        },
+        "user_id": {
+          "title": "User Id",
+          "type": "string"
+        },
+        "org_id": {
+          "title": "Org Id",
+          "type": "string"
+        },
+        "team_id": {
+          "title": "Team Id",
+          "type": "string"
+        },
+        "group_id": {
+          "title": "Group Id",
+          "type": "string"
+        },
+        "author_id": {
+          "title": "Author Id",
+          "type": "string"
+        },
+        "path_name": {
+          "title": "Path Name",
+          "type": "string"
+        },
+        "build_id": {
+          "title": "Build Id",
+          "type": "string"
+        },
+        "build_name": {
+          "title": "Build Name",
+          "type": "string"
+        },
+        "creation_time": {
+          "title": "Creation Time",
+          "type": "string",
+          "format": "date-time"
+        },
+        "start_time": {
+          "title": "Start Time",
+          "type": "string",
+          "format": "date-time"
+        },
+        "end_time": {
+          "title": "End Time",
+          "type": "string",
+          "format": "date-time"
+        },
+        "duration": {
+          "title": "Duration",
+          "type": "integer"
+        },
+        "browser": {
+          "title": "Browser",
+          "type": "string"
+        },
+        "device": {
+          "title": "Device",
+          "type": "string"
+        },
+        "os": {
+          "title": "Os",
+          "type": "string"
+        },
+        "app_name": {
+          "title": "App Name",
+          "type": "string"
+        },
+        "status": {
+          "type": "string",
+          "allOf": [
+            {
+              "$ref": "#/definitions/Status"
+            }
+          ],
+          "default": "skipped"
+        },
+        "platform": {
+          "type": "string",
+          "allOf": [
+            {
+              "$ref": "#/definitions/TestRunPlatform"
+            }
+          ],
+          "default": "other"
+        },
+        "type": {
+          "type": "string",
+          "allOf": [
+            {
+              "$ref": "#/definitions/TestType"
+            }
+          ],
+          "default": "other"
+        },
+        "framework": {
+          "title": "Framework",
+          "type": "string"
+        },
+        "ci": {
+          "$ref": "#/definitions/Ci"
+        },
+        "api_testing": {
+          "$ref": "#/definitions/ApiTesting"
+        },
+        "sauce_job": {
+          "$ref": "#/definitions/SauceJob"
+        },
+        "errors": {
+          "title": "Errors",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/TestRunTestError"
+          }
+        },
+        "tags": {
+          "title": "Tags",
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      }
+    },
+    "TestRuns": {
+      "type": "object",
+      "properties": {
+        "test_runs": {
+          "title": "TestRuns",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/TestRun"
+          }
+        }
+      }
+    },
+    "TestType": {
+      "title": "TestType",
+      "enum": ["web", "mobile", "api", "other"],
+      "type": "string",
+      "description": "An enumeration."
+    },
+    "ValidationError": {
+      "title": "ValidationError",
+      "required": ["loc", "msg", "type"],
+      "type": "object",
+      "properties": {
+        "loc": {
+          "title": "Location",
+          "type": "array",
+          "items": {
+            "anyOf": [
+              {
+                "type": "string"
+              },
+              {
+                "type": "integer"
+              }
+            ]
+          }
+        },
+        "msg": {
+          "title": "Message",
+          "type": "string"
+        },
+        "type": {
+          "title": "Error Type",
+          "type": "string"
+        }
+      }
+    }
+  }
+}
diff --git a/tests/index.test.js b/tests/index.test.js
index 88dd00d..afc0ff0 100644
--- a/tests/index.test.js
+++ b/tests/index.test.js
@@ -727,6 +727,37 @@ test('should stringify searchParams', () => {
   expect(got.mock.calls[0][1].searchParams).toEqual('id=job-1&id=job-2');
 });
 
+test('should get HTTPValidationError when posting test-runs failed', async () => {
+  const api = new SauceLabs({user: 'foo', key: 'bar'});
+  got.mockReturnValue(
+    Promise.resolve({
+      body: {
+        detail: [
+          {
+            loc: ['body', 14],
+            msg: 'Expecting property name enclosed in double quotes: line 1 column 15 (char 14)',
+            type: 'value_error.jsondecode',
+            ctx: {
+              msg: 'Expecting property name enclosed in double quotes',
+              doc: '...',
+              pos: 14,
+              lineno: 1,
+              colno: 15,
+            },
+          },
+        ],
+      },
+    })
+  );
+  const failedResp = await api.createTestRunsV1({testRuns: []});
+  expect(failedResp.detail.length).toEqual(1);
+  const detail = failedResp.detail[0];
+  expect(detail.type).toEqual('value_error.jsondecode');
+  expect(detail.msg).toEqual(
+    'Expecting property name enclosed in double quotes: line 1 column 15 (char 14)'
+  );
+});
+
 afterEach(() => {
   fs.writeFileSync.mockClear();
   got.mockClear();