wdio-performancetotal-service
Advanced tools
Comparing version 3.0.0 to 3.1.0
@@ -1,14 +0,15 @@ | ||
declare class FileWriter { | ||
/** | ||
* Overwrites data to file | ||
* @param filePath | ||
* @param content | ||
*/ | ||
writeToFile(filePath: string, content: string): Promise<void>; | ||
appendLineToFile(filePath: string, lineContent: string): Promise<void>; | ||
readAllLines(filePath: string): Promise<Array<string>>; | ||
makeDir(dirPath: string): Promise<void>; | ||
isFileExist(dirPath: string): Promise<boolean>; | ||
export declare class FileWriter { | ||
private static instance; | ||
private lock; | ||
private constructor(); | ||
static getInstance(): FileWriter; | ||
readAllLines(path: string): Promise<Array<string>>; | ||
writeToFile(path: string, data: string): Promise<void>; | ||
appendLineToFile(path: string, data: string): Promise<void>; | ||
getFilePath(resultsDir: string, fileName: string): string; | ||
createResultsDirIfNotExist(resultsPath?: string): Promise<string>; | ||
private makeDir; | ||
private isFileExist; | ||
private lockFile; | ||
private unlockFile; | ||
} | ||
declare const _default: FileWriter; | ||
export default _default; |
@@ -11,43 +11,93 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.FileWriter = void 0; | ||
const fs_1 = require("fs"); | ||
const path_1 = __importDefault(require("path")); | ||
const app_root_path_1 = __importDefault(require("app-root-path")); | ||
class FileWriter { | ||
/** | ||
* Overwrites data to file | ||
* @param filePath | ||
* @param content | ||
*/ | ||
writeToFile(filePath, content) { | ||
constructor() { | ||
this.lock = Promise.resolve(); | ||
} | ||
static getInstance() { | ||
if (!FileWriter.instance) { | ||
FileWriter.instance = new FileWriter(); | ||
} | ||
return FileWriter.instance; | ||
} | ||
readAllLines(path) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield this.lock; | ||
let data = ""; | ||
try { | ||
yield fs_1.promises.writeFile(filePath, content); | ||
this.lock = this.lockFile(); | ||
data = yield fs_1.promises.readFile(path, "utf-8"); | ||
} | ||
catch (err) { | ||
console.log(`Performance-Total error: writeFile failed: ${err}`); | ||
catch (error) { | ||
console.error(`An error occurred while reading file ${path}:`, error); | ||
} | ||
finally { | ||
yield this.unlockFile(); | ||
} | ||
const stringArray = data.split("\n"); | ||
return stringArray; | ||
}); | ||
} | ||
appendLineToFile(filePath, lineContent) { | ||
writeToFile(path, data) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield this.lock; | ||
try { | ||
yield fs_1.promises.appendFile(filePath, lineContent); | ||
this.lock = this.lockFile(); | ||
yield fs_1.promises.writeFile(path, data); | ||
} | ||
catch (err) { | ||
console.log(`Performance-Total error: appendFile failed: ${err}`); | ||
catch (error) { | ||
console.error(`An error occurred while writing file ${path}:`, error); | ||
} | ||
finally { | ||
yield this.unlockFile(); | ||
} | ||
}); | ||
} | ||
readAllLines(filePath) { | ||
appendLineToFile(path, data) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let data = ""; | ||
yield this.lock; | ||
try { | ||
data = yield fs_1.promises.readFile(filePath, "utf-8"); | ||
this.lock = this.lockFile(); | ||
yield fs_1.promises.appendFile(path, data); | ||
} | ||
catch (err) { | ||
console.log(`Performance-Total error: readFile failed: ${err}`); | ||
catch (error) { | ||
console.error(`An error occurred while appending file ${path}:`, error); | ||
} | ||
const stringArray = data.split("\n"); | ||
return stringArray; | ||
finally { | ||
yield this.unlockFile(); | ||
} | ||
}); | ||
} | ||
getFilePath(resultsDir, fileName) { | ||
return path_1.default.join(resultsDir, fileName); | ||
} | ||
createResultsDirIfNotExist(resultsPath) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let npath = ""; | ||
const root = app_root_path_1.default.path; | ||
let isNotLegal = true; | ||
if (resultsPath) { | ||
isNotLegal = /[*"\[\]:;|,]/g.test(resultsPath); | ||
npath = path_1.default.normalize(resultsPath); | ||
} | ||
const resultsDir = npath == undefined || npath == "" || isNotLegal ? "performance-results" : npath; | ||
if (!root) { | ||
console.log("Performance-Total error: Can't get root folder"); | ||
return ""; | ||
} | ||
const dirPath = path_1.default.join(root, resultsDir); | ||
const isFileExists = yield this.isFileExist(dirPath); | ||
if (!isFileExists) { | ||
yield this.makeDir(dirPath); | ||
} | ||
return dirPath; | ||
}); | ||
} | ||
makeDir(dirPath) { | ||
@@ -59,3 +109,3 @@ return __awaiter(this, void 0, void 0, function* () { | ||
catch (err) { | ||
console.log(`Performance-Total error: can't create dir: ${dirPath}: ${err}`); | ||
console.log(`Performance-Total error: can't create dir: ${dirPath}: ${err}, ${err.stack}`); | ||
} | ||
@@ -71,9 +121,22 @@ }); | ||
} | ||
catch (_a) { | ||
isExists = false; | ||
finally { | ||
return isExists; | ||
} | ||
return isExists; | ||
}); | ||
} | ||
lockFile() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield this.lock; | ||
this.lock = new Promise((resolve) => { | ||
setImmediate(resolve); | ||
}); | ||
}); | ||
} | ||
unlockFile() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield this.lock; | ||
this.lock = Promise.resolve(); | ||
}); | ||
} | ||
} | ||
exports.default = new FileWriter(); | ||
exports.FileWriter = FileWriter; |
@@ -18,3 +18,3 @@ "use strict"; | ||
const calculator_1 = __importDefault(require("./helpers/calculator")); | ||
const file_writer_1 = __importDefault(require("./helpers/file-writer")); | ||
const file_writer_1 = require("./helpers/file-writer"); | ||
const group_1 = __importDefault(require("./helpers/group")); | ||
@@ -34,2 +34,5 @@ const objects_to_csv_1 = __importDefault(require("objects-to-csv")); | ||
} | ||
if (!performanceLogEntries || performanceLogEntries.length == 0) { | ||
return; | ||
} | ||
groupedResults = !analyzeByBrowser ? group_1.default.groupBy(performanceLogEntries, p => [p.name]) : group_1.default.groupBy(performanceLogEntries, p => [p.name, p.brName]); | ||
@@ -60,6 +63,7 @@ groupedResults.forEach(group => { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield file_writer_1.default.writeToFile(saveDataFilePath + ".json", JSON.stringify(this._performanceResults)); | ||
const fileWriter = file_writer_1.FileWriter.getInstance(); | ||
yield fileWriter.writeToFile(saveDataFilePath + ".json", JSON.stringify(this._performanceResults)); | ||
const csv = new objects_to_csv_1.default(this._performanceResults); | ||
const csvString = yield csv.toString(true); | ||
yield file_writer_1.default.writeToFile(saveDataFilePath + ".csv", csvString); | ||
yield fileWriter.writeToFile(saveDataFilePath + ".csv", csvString); | ||
}); | ||
@@ -70,3 +74,3 @@ } | ||
const resultsArray = new Array(); | ||
const textResultsArray = yield file_writer_1.default.readAllLines(fileName); | ||
const textResultsArray = yield file_writer_1.FileWriter.getInstance().readAllLines(fileName); | ||
textResultsArray.forEach(textResult => { | ||
@@ -73,0 +77,0 @@ if (textResult != "") { |
@@ -1,10 +0,7 @@ | ||
/// <reference types="@wdio/globals/types" /> | ||
declare class PerformanceTotal { | ||
private _instanceid; | ||
private _resultsDir; | ||
private logFileName; | ||
private performanceCache; | ||
private _performanceResultsFileName; | ||
private performanceLogger; | ||
constructor(appendToExistingFile?: boolean); | ||
get outDir(): string; | ||
sampleStart(stepName: string): void; | ||
@@ -22,3 +19,3 @@ sampleEnd(stepName: string): void; | ||
*/ | ||
finalize(browser: WebdriverIO.Browser, isTestPassed: boolean): void; | ||
finalizeTest(browser: WebdriverIO.Browser, isTestPassed: boolean): Promise<void>; | ||
/** | ||
@@ -31,4 +28,2 @@ * @deprecated Don't use this method if *wdio-performancetotal-service* is enabled. | ||
analyzeResults({ performanceResultsFileName, dropResultsFromFailedTest, analyzeByBrowser }: initializeParams): Promise<void>; | ||
private getFilePath; | ||
private createResultsDirIfNotExist; | ||
} | ||
@@ -35,0 +30,0 @@ declare const _default: PerformanceTotal; |
@@ -15,26 +15,22 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const performance_logger_1 = require("./performance-logger"); | ||
const path_1 = __importDefault(require("path")); | ||
const file_writer_1 = __importDefault(require("./helpers/file-writer")); | ||
const file_writer_1 = require("./helpers/file-writer"); | ||
const id_generator_1 = require("./helpers/id-generator"); | ||
const app_root_path_1 = __importDefault(require("app-root-path")); | ||
const performance_analyzer_1 = require("./performance-analyzer"); | ||
const performance_cache_1 = require("./performance-cache"); | ||
class PerformanceTotal { | ||
constructor(appendToExistingFile = false) { | ||
this._resultsDir = ""; | ||
this.logFileName = "performance-log.txt"; | ||
this._performanceResultsFileName = "performance-results"; | ||
this.performanceCache = new performance_cache_1.PerformanceCache(); | ||
this._instanceid = new id_generator_1.IdGenerator().getId("inst"); | ||
this.performanceLogger = new performance_logger_1.PerformanceLogger(); | ||
} | ||
get outDir() { | ||
return this._resultsDir; | ||
} | ||
sampleStart(stepName) { | ||
this.performanceLogger.sampleStart(stepName, this._instanceid); | ||
this.performanceCache.sampleStart(stepName, this._instanceid); | ||
} | ||
sampleEnd(stepName) { | ||
this.performanceLogger.sampleEnd(stepName, this._instanceid); | ||
this.performanceCache.sampleEnd(stepName, this._instanceid); | ||
} | ||
getSampleTime(stepName) { | ||
return this.performanceLogger.getSampleTime(stepName); | ||
return this.performanceCache.getSampleTime(stepName); | ||
} | ||
@@ -47,10 +43,18 @@ /** | ||
return __awaiter(this, void 0, void 0, function* () { | ||
this._resultsDir = yield this.createResultsDirIfNotExist(performanceResultsDirectory); | ||
let resultsDir = ""; | ||
const fileWriter = file_writer_1.FileWriter.getInstance(); | ||
if (!global._performanceTotalResultsDir) { | ||
resultsDir = yield fileWriter.createResultsDirIfNotExist(performanceResultsDirectory); | ||
global._performanceTotalResultsDir = resultsDir; | ||
} | ||
else { | ||
resultsDir = global._performanceTotalResultsDir; | ||
} | ||
const initObj = JSON.stringify({ "startDisplayTime": new Date().toLocaleString(), "instanceID": this._instanceid }); | ||
const fileName = path_1.default.join(this._resultsDir, this.logFileName); | ||
const fileName = path_1.default.join(resultsDir, this.logFileName); | ||
if (disableAppendToExistingFile) { | ||
yield file_writer_1.default.writeToFile(fileName, `${initObj}\n`); | ||
yield fileWriter.writeToFile(fileName, `${initObj}\n`); | ||
} | ||
else { | ||
yield file_writer_1.default.appendLineToFile(fileName, `${initObj}\n`); | ||
yield fileWriter.appendLineToFile(fileName, `${initObj}\n`); | ||
} | ||
@@ -63,4 +67,6 @@ }); | ||
*/ | ||
finalize(browser, isTestPassed) { | ||
this.performanceLogger.flush(this.getFilePath(this.logFileName), browser, isTestPassed); | ||
finalizeTest(browser, isTestPassed) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield this.performanceCache.flush(file_writer_1.FileWriter.getInstance().getFilePath(global._performanceTotalResultsDir, this.logFileName), browser, isTestPassed); | ||
}); | ||
} | ||
@@ -75,35 +81,13 @@ /** | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const analyzer = new performance_analyzer_1.PerformanceAnalyzer(); | ||
const fileWriter = file_writer_1.FileWriter.getInstance(); | ||
let resultsFileName = this._performanceResultsFileName; | ||
const resultsDir = global._performanceTotalResultsDir; | ||
if (performanceResultsFileName) { | ||
resultsFileName = performanceResultsFileName; | ||
} | ||
yield this.performanceLogger.analyzeResults(this.getFilePath(this.logFileName), this.getFilePath(resultsFileName), dropResultsFromFailedTest, analyzeByBrowser); | ||
yield analyzer.analyze(fileWriter.getFilePath(resultsDir, this.logFileName), fileWriter.getFilePath(resultsDir, resultsFileName), dropResultsFromFailedTest, analyzeByBrowser); | ||
}); | ||
} | ||
getFilePath(fileName) { | ||
return path_1.default.join(this._resultsDir, fileName); | ||
} | ||
createResultsDirIfNotExist(resultsPath) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let npath = ""; | ||
let isNotLegal = true; | ||
if (resultsPath) { | ||
isNotLegal = /[*"\[\]:;|,]/g.test(resultsPath); | ||
npath = path_1.default.normalize(resultsPath); | ||
} | ||
const resultsDir = npath == undefined || npath == "" || isNotLegal ? "performance-results" : npath; | ||
const root = app_root_path_1.default.path; | ||
if (!root) { | ||
console.log("Performance-Total error: Can't get root folder"); | ||
return ""; | ||
} | ||
const dirPath = path_1.default.join(root, resultsDir); | ||
const isFileExists = yield file_writer_1.default.isFileExist(dirPath); | ||
if (!isFileExists) { | ||
yield file_writer_1.default.makeDir(dirPath); | ||
} | ||
return dirPath; | ||
}); | ||
} | ||
} | ||
exports.default = new PerformanceTotal(); |
@@ -1,11 +0,5 @@ | ||
/// <reference types="@wdio/globals/types" /> | ||
import { Options } from "./entities/options"; | ||
export default class PerformanceTotalService { | ||
_browser: WebdriverIO.Browser; | ||
_serviceOptions: { | ||
disableAppendToExistingFile: boolean; | ||
performanceResultsFileName: string; | ||
dropResultsFromFailedTest: boolean; | ||
analyzeByBrowser: boolean; | ||
performanceResultsDirectory: string; | ||
}; | ||
_serviceOptions: Options; | ||
/** | ||
@@ -21,12 +15,4 @@ * `serviceOptions` contains all options specific to the service | ||
*/ | ||
constructor(serviceOptions: { | ||
disableAppendToExistingFile: boolean; | ||
performanceResultsFileName: string; | ||
dropResultsFromFailedTest: boolean; | ||
analyzeByBrowser: boolean; | ||
performanceResultsDirectory: string; | ||
}, capabilities: any, config: any); | ||
before(config: any, capabilities: any, browser: WebdriverIO.Browser): void; | ||
beforeTest(test: any, context: any): Promise<void>; | ||
beforeScenario(test: any, context: any): Promise<void>; | ||
constructor(serviceOptions: Options, capabilities: any, config: any); | ||
before(config: any, capabilities: any, browser: WebdriverIO.Browser): Promise<void>; | ||
afterTest(test: any, context: any, { error, result, duration, passed, retries }: { | ||
@@ -38,5 +24,5 @@ error: any; | ||
retries: any; | ||
}): void; | ||
afterScenario(test: any, context: any): void; | ||
}): Promise<void>; | ||
afterScenario(test: any, context: any): Promise<void>; | ||
after(exitCode: any, config: any, capabilities: any): Promise<void>; | ||
} |
@@ -41,20 +41,17 @@ "use strict"; | ||
before(config, capabilities, browser) { | ||
this._browser = browser; | ||
} | ||
beforeTest(test, context) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
this._browser = browser; | ||
yield performance_total_1.default.initialize(this._serviceOptions.disableAppendToExistingFile, this._serviceOptions.performanceResultsDirectory); | ||
}); | ||
} | ||
beforeScenario(test, context) { | ||
//@ts-ignore | ||
afterTest(test, context, { error, result, duration, passed, retries }) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield performance_total_1.default.initialize(this._serviceOptions.disableAppendToExistingFile, this._serviceOptions.performanceResultsDirectory); | ||
yield performance_total_1.default.finalizeTest(this._browser, passed); | ||
}); | ||
} | ||
//@ts-ignore | ||
afterTest(test, context, { error, result, duration, passed, retries }) { | ||
performance_total_1.default.finalize(this._browser, passed); | ||
} | ||
afterScenario(test, context) { | ||
performance_total_1.default.finalize(this._browser, test.result.status == Status.PASSED); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield performance_total_1.default.finalizeTest(this._browser, test.result.status == Status.PASSED); | ||
}); | ||
} | ||
@@ -61,0 +58,0 @@ after(exitCode, config, capabilities) { |
{ | ||
"name": "wdio-performancetotal-service", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "WebdriverIO service for analyzing test flow performance", | ||
@@ -5,0 +5,0 @@ "main": "build/app.js", |
@@ -11,13 +11,8 @@ # performance-total | ||
With this plugin for [webdriver.io](https://webdriver.io/) you can easily add performance analysis to any flow in your tests. | ||
With this plugin for [webdriver.io](https://webdriver.io/) you can easily add performance analysis to any flow in your tests, whether it's a pure UI, API, or a combination of both. This plugin provides a simple and efficient way to measure the response times of various procedures and identify potential bottlenecks in your application. With this information, you can make informed decisions about optimizations and improvements to enhance the overall performance of your application. | ||
<h2>Installation</h2> | ||
The easiest way to install this module as a (dev-)dependency is by using the following command: | ||
The easiest way to install this module as a dev dependency is by using the following command: | ||
``` | ||
npm install wdio-performancetotal-service --save | ||
``` | ||
Or: | ||
``` | ||
npm install wdio-performancetotal-service --save-dev | ||
@@ -61,3 +56,3 @@ ``` | ||
When set to `true`, new test runs or tests from another spec file will overwrite any existing performance data. | ||
When set to `true`, new test runs will start fresh and overwrite any existing performance data. | ||
When set to `false` (default), performance data will be added to the existing data. | ||
@@ -96,31 +91,40 @@ | ||
Just import <b>performancetotal</b> where you need it, whether it be your test file or any other class: | ||
Just import <b>performancetotal</b> where you need it, whether it be in your test file or any other class. This object provides methods for measuring performance data in your tests, including sampleStart and sampleEnd for starting and ending performance measurements. | ||
Here's an example of how you might use the performancetotal object to measure the startup performance of two websites: | ||
``` | ||
// This test case measures the startup performance of Github and SourceForge using the performancetotal object. | ||
import { performancetotal } from "wdio-performancetotal-service"; | ||
it("should test github startup performance", () => { | ||
// ... | ||
performancetotal.sampleStart("Startup"); | ||
browser.url("https://github.com/"); | ||
performancetotal.sampleEnd("Startup"); | ||
//... | ||
}); | ||
it("should test github and sourceforge startup performance", () => { | ||
// Start a new performance measurement for Github | ||
performancetotal.sampleStart("GH-Startup"); | ||
// Navigate to Github | ||
browser.url("https://github.com/"); | ||
// End the Github measurement and save the results | ||
performancetotal.sampleEnd("GH-Startup"); | ||
// ... | ||
// Start a new performance measurement for SourceForge | ||
performancetotal.sampleStart("SF-Startup"); | ||
// Navigate to SourceForge | ||
await browser.url("https://sourceforge.net/"); | ||
// End the SourceForge measurement and save the results | ||
performancetotal.sampleEnd("SF-Startup"); | ||
}); | ||
``` | ||
It is possible to get the time span for a single sample inside a test: | ||
You can retrieve the time taken for a single performance sample by calling performancetotal.getSampleTime(sampleName) in your test. This allows you to check the performance of a specific section of code and ensure that it meets your expectations. | ||
``` | ||
it("should test github startup performance", () => { | ||
// ... | ||
performancetotal.sampleStart("Startup"); | ||
browser.url("https://github.com/"); | ||
performancetotal.sampleEnd("Startup"); | ||
// Get the time taken for a single sample | ||
const sampleTime = performancetotal.getSampleTime(sampleName); | ||
expect(performancetotal.getSampleTime("Startup")).to.be.at.most(1000); | ||
}); | ||
``` | ||
@@ -130,3 +134,3 @@ | ||
A new results directory (the default directory name is `performance-results`) is created in your project's root folder and when all the tests are completed two files are created inside it: `performance-results.json` and `performance-results.csv`. The analyzed data includes: average time, standard error of mean(sem), number of samples, min value, max value, earliest time and latest time. | ||
When all the tests are completed, a new results directory is created in your project's root folder (the default directory name is performance-results). Inside this directory, two files are created: performance-results.json and performance-results.csv. These files contain analyzed data for each sample, including the average time, standard error of mean (SEM), number of samples, minimum value, maximum value, earliest time, and latest time. You can use this data to identify any performance regressions or improvements over time. | ||
@@ -133,0 +137,0 @@ <h2>Typescript support</h2> |
40145
802
135