Skip to content

Commit

Permalink
feat: Automatic asset uploading (#84)
Browse files Browse the repository at this point in the history
* feat: Automatic asset upload

* deps: Bump @saucelabs/testcomposer

* docs: Describe artifactUploadDir

* Clean artifact upload dir on run start

* Use `path` over `parentPath`

Technically `path` is deprecated, but its replacement, `parentPath`
is still considered experimental.

* Note about where the artifact dir is relative to

* Actually just avoid path/parentPath for now

* Fix weird mix of camel and snake case

* update node engine to 18

* Check exact dir existence before readdir call

* Add note to readme that artifactUploadDir gets deleted at the next run
  • Loading branch information
mhan83 authored Oct 23, 2024
1 parent 1188832 commit 0c12286
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 16 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ module.exports = (on, config) => {
};
```

## Plugin Options

| Name | Description | Type |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `build` | Sets a build ID. <br> Default: `''` | `string` |
| `tags` | Tags to add to the uploaded Sauce job. <br> Default: `[]` | `string[]` |
| `region` | Sets the region. <br> Default: `us-west-1` | `us-west-1` \| `eu-central-1` |
| `artifactUploadDir` | If specified, automatically upload files from this directory, **per spec**. e.g. files in `{artifactUploadDir}/{specName}/` would be uploaded to the job that ran `spec_name`. The directory is relative to your cypress config file. The directory will be deleted at the beginning of the next run. Default: `undefined` | `string` |

## Run a Test 🚀

Trigger cypress to run a test
Expand Down
13 changes: 6 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@
],
"license": "MIT",
"engines": {
"node": ">=16.13.2"
"node": ">=18.0.0"
},
"dependencies": {
"@saucelabs/sauce-json-reporter": "4.1.0",
"@saucelabs/testcomposer": "3.0.1",
"@saucelabs/testcomposer": "3.0.2",
"axios": "^1.7.5",
"chalk": "^4.1.2",
"cli-table3": "^0.6.5",
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface Options {
build?: string;
tags?: string[];
webAssetsDir?: string;

artifactUploadDir?: string;
}

let reporterInstance: Reporter;
Expand Down Expand Up @@ -129,7 +131,7 @@ export async function afterRunTestReport(
}

const reportJSON = await rep.createSauceTestReport(testResults);
const assets = rep.collectAssets(testResults, reportJSON);
const assets = await rep.collectAssets(testResults, reportJSON);
rep.syncAssets(assets);
return reportJSON;
}
Expand Down
40 changes: 34 additions & 6 deletions src/reporter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'fs';
import path from 'path';
import * as stream from 'stream';
import * as fs from 'node:fs';
import { readdir } from 'node:fs/promises';
import path from 'node:path';
import * as stream from 'node:stream';

import * as Cypress from 'cypress';
import BeforeRunDetails = Cypress.BeforeRunDetails;
Expand Down Expand Up @@ -97,6 +98,10 @@ export default class Reporter {
fs.mkdirSync(this.webAssetsDir, { recursive: true });
}

if (opts.artifactUploadDir && fs.existsSync(opts.artifactUploadDir)) {
fs.rmSync(opts.artifactUploadDir, { recursive: true, force: true });
}

if (process.env.SAUCE_VM) {
return;
}
Expand Down Expand Up @@ -141,7 +146,7 @@ export default class Reporter {
});

const report = await this.createSauceTestReport([result]);
const assets = this.collectAssets([result], report);
const assets = await this.collectAssets([result], report);
await this.uploadAssets(job.id, assets);

return job;
Expand Down Expand Up @@ -420,11 +425,11 @@ export default class Reporter {
* Gathers test assets for upload to Sauce through the TestComposer API.
* Assets include videos, screenshots, console logs, and the Sauce JSON report.
*
* @param {RunResult[]} result - Contains video and screenshot paths from a Cypress test run.
* @param {RunResult[]} results - Contains video and screenshot paths from a Cypress test run.
* @param {TestRun} report - The Sauce JSON report object.
* @returns {Asset[]} Array of assets, each with a filename and data stream, ready for upload.
*/
collectAssets(results: RunResult[], report: TestRun): Asset[] {
async collectAssets(results: RunResult[], report: TestRun): Promise<Asset[]> {
const assets: Asset[] = [];
for (const result of results) {
const specName = result.spec.name;
Expand All @@ -442,6 +447,29 @@ export default class Reporter {
data: fs.createReadStream(s.path),
});
});

if (this.opts.artifactUploadDir) {
const artifactPath = path.join(this.opts.artifactUploadDir, specName);
if (fs.existsSync(artifactPath)) {
const entries = await readdir(path.resolve(artifactPath), {
withFileTypes: true,
});

for (const entry of entries) {
if (!entry.isFile()) {
continue;
}

const entryPath = path.join(artifactPath, entry.name);
assets.push({
filename: entry.name,
path: entryPath,
data: fs.createReadStream(path.resolve(entryPath)),
});
}
}
}

assets.push(
{
data: this.ReadableStream(this.getConsoleLog(result)),
Expand Down

0 comments on commit 0c12286

Please sign in to comment.