wdio-lambdatest-service-sdk 5.0.0 → 5.1.0
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/README.md +339 -36
- package/bin/cli.js +86 -0
- package/bin/generate-config.js +14 -166
- package/bin/setup.js +14 -128
- package/bin/wdio-lt.js +39 -0
- package/bun.lock +875 -0
- package/index.js +4 -5
- package/lib/cli/generate.js +526 -0
- package/lib/cli/setup.js +174 -0
- package/lib/cli/style.js +57 -0
- package/package.json +24 -4
- package/src/launcher.js +52 -29
- package/src/service.js +36 -40
package/lib/cli/setup.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup command logic: Inject LambdaTest service into WDIO config files.
|
|
3
|
+
*/
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const inquirer = require('inquirer');
|
|
8
|
+
|
|
9
|
+
// SDK path relative to this file (lib/cli/setup.js -> root)
|
|
10
|
+
const sdkPath = path.resolve(__dirname, '../..');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse existing user/key from config file content (supports commented lines).
|
|
14
|
+
* @param {string} content - File content
|
|
15
|
+
* @returns {{ userVal: string, keyVal: string }}
|
|
16
|
+
*/
|
|
17
|
+
function parseCredentialsFromContent(content) {
|
|
18
|
+
let userVal = 'process.env.LT_USERNAME';
|
|
19
|
+
let keyVal = 'process.env.LT_ACCESS_KEY';
|
|
20
|
+
const userMatch = content.match(/^\s*\/{0,2}\s*user:\s*(.*?),/m);
|
|
21
|
+
if (userMatch) userVal = userMatch[1].trim();
|
|
22
|
+
const keyMatch = content.match(/^\s*\/{0,2}\s*key:\s*(.*?),/m);
|
|
23
|
+
if (keyMatch) keyVal = keyMatch[1].trim();
|
|
24
|
+
return { userVal, keyVal };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Update a single WDIO config file: inject service, ensure path, set logLevel, comment user/key.
|
|
29
|
+
* Avoids double-injection by checking for existing wdio-lambdatest-service.
|
|
30
|
+
*/
|
|
31
|
+
function updateConfigFile(filePath) {
|
|
32
|
+
let content;
|
|
33
|
+
try {
|
|
34
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error(chalk.red(` ✖ Failed to read ${filePath}:`), e.message);
|
|
37
|
+
throw new Error(`Failed to read ${filePath}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const relativePath = path.relative(process.cwd(), filePath);
|
|
41
|
+
console.log(chalk.dim(` • Processing: `) + relativePath);
|
|
42
|
+
|
|
43
|
+
const { userVal, keyVal } = parseCredentialsFromContent(content);
|
|
44
|
+
|
|
45
|
+
let relativeSdkPath = path.relative(path.dirname(filePath), sdkPath);
|
|
46
|
+
relativeSdkPath = relativeSdkPath.replace(/\\/g, '/');
|
|
47
|
+
|
|
48
|
+
const serviceEntry = `[path.join(__dirname, '${relativeSdkPath}'), { user: ${userVal}, key: ${keyVal} }]`;
|
|
49
|
+
|
|
50
|
+
if (content.includes('wdio-lambdatest-service')) {
|
|
51
|
+
const emptyOptionsRegex = /\[path\.join\(__dirname, '.*?wdio-lambdatest-service'\), \{\}\]/;
|
|
52
|
+
if (emptyOptionsRegex.test(content)) {
|
|
53
|
+
console.log(chalk.yellow(` ⚠ Fixing missing credentials in: `) + relativePath);
|
|
54
|
+
content = content.replace(emptyOptionsRegex, serviceEntry);
|
|
55
|
+
content = content.replace(/logLevel: ['"]info['"]/, "logLevel: 'error'");
|
|
56
|
+
try {
|
|
57
|
+
fs.writeFileSync(filePath, content);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error(chalk.red(` ✖ Failed to write ${filePath}:`), e.message);
|
|
60
|
+
throw new Error(`Failed to write ${filePath}`);
|
|
61
|
+
}
|
|
62
|
+
console.log(chalk.green(` ✔ Successfully repaired `) + relativePath);
|
|
63
|
+
} else {
|
|
64
|
+
console.log(chalk.dim(` - Skipping (already correct): `) + relativePath);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(chalk.cyan(` → Updating: `) + relativePath);
|
|
70
|
+
|
|
71
|
+
if (!content.includes("require('path')")) {
|
|
72
|
+
content = "const path = require('path');\n" + content;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (content.includes('services: [')) {
|
|
76
|
+
content = content.replace('services: [', `services: [\n ${serviceEntry},`);
|
|
77
|
+
} else {
|
|
78
|
+
content = content.replace('exports.config = {', `exports.config = {\n services: [\n ${serviceEntry}\n ],`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (content.includes('logLevel:')) {
|
|
82
|
+
content = content.replace(/logLevel:.*,/, "logLevel: 'error',");
|
|
83
|
+
} else {
|
|
84
|
+
content = content.replace('exports.config = {', `exports.config = {\n logLevel: 'error',`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
content = content.replace(/^(\s*)user:/gm, '$1// user:');
|
|
88
|
+
content = content.replace(/^(\s*)key:/gm, '$1// key:');
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
fs.writeFileSync(filePath, content);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.error(chalk.red(` ✖ Failed to write ${filePath}:`), e.message);
|
|
94
|
+
throw new Error(`Failed to write ${filePath}`);
|
|
95
|
+
}
|
|
96
|
+
console.log(chalk.green(` ✔ Successfully updated `) + relativePath);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function traverseDirectory(dir) {
|
|
100
|
+
if (!fs.existsSync(dir)) {
|
|
101
|
+
console.error(chalk.red(`✖ Directory not found: ${dir}`));
|
|
102
|
+
throw new Error(`Directory not found: ${dir}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const files = fs.readdirSync(dir);
|
|
106
|
+
for (const file of files) {
|
|
107
|
+
const fullPath = path.join(dir, file);
|
|
108
|
+
let stat;
|
|
109
|
+
try {
|
|
110
|
+
stat = fs.statSync(fullPath);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
console.error(chalk.dim(` ! Cannot read ${fullPath}: ${e.message}`));
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (stat.isDirectory()) {
|
|
116
|
+
if (file !== 'node_modules' && file !== '.git') {
|
|
117
|
+
traverseDirectory(fullPath);
|
|
118
|
+
}
|
|
119
|
+
} else if (file.endsWith('.conf.js')) {
|
|
120
|
+
updateConfigFile(fullPath);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function printBanner() {
|
|
126
|
+
console.log();
|
|
127
|
+
console.log(chalk.cyan.bold('╭─────────────────────────────────────╮'));
|
|
128
|
+
console.log(chalk.cyan.bold('│') + ' 🚀 ' + chalk.bold('LambdaTest WDIO Setup') + ' ' + chalk.cyan.bold('│'));
|
|
129
|
+
console.log(chalk.cyan.bold('╰─────────────────────────────────────╯'));
|
|
130
|
+
console.log();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Run the setup command.
|
|
135
|
+
* @param {string|undefined} targetPath - Path to directory containing .conf.js files. If undefined, prompts interactively.
|
|
136
|
+
* @returns {Promise<void>}
|
|
137
|
+
*/
|
|
138
|
+
async function runSetup(targetPath) {
|
|
139
|
+
printBanner();
|
|
140
|
+
|
|
141
|
+
let resolvedPath;
|
|
142
|
+
|
|
143
|
+
if (targetPath) {
|
|
144
|
+
resolvedPath = path.resolve(targetPath.trim());
|
|
145
|
+
} else {
|
|
146
|
+
// Interactive mode using inquirer
|
|
147
|
+
const answers = await inquirer.default.prompt([
|
|
148
|
+
{
|
|
149
|
+
type: 'input',
|
|
150
|
+
name: 'path',
|
|
151
|
+
message: 'Where are your test scripts located?',
|
|
152
|
+
default: './android-sample',
|
|
153
|
+
validate: (input) => {
|
|
154
|
+
if (!input || input.trim() === '') {
|
|
155
|
+
return 'Please enter a valid path';
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
]);
|
|
161
|
+
resolvedPath = path.resolve(answers.path.trim());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(chalk.cyan('→ Scanning:'), chalk.dim(resolvedPath));
|
|
165
|
+
console.log();
|
|
166
|
+
|
|
167
|
+
traverseDirectory(resolvedPath);
|
|
168
|
+
|
|
169
|
+
console.log();
|
|
170
|
+
console.log(chalk.green.bold('✔ Done scanning directories!'));
|
|
171
|
+
console.log();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = { runSetup };
|
package/lib/cli/style.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared style utilities using Chalk for CLI output.
|
|
3
|
+
* Provides consistent, beautiful terminal styling.
|
|
4
|
+
*/
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
|
|
7
|
+
const style = {
|
|
8
|
+
// Colors
|
|
9
|
+
reset: chalk.reset,
|
|
10
|
+
bold: chalk.bold,
|
|
11
|
+
dim: chalk.dim,
|
|
12
|
+
cyan: chalk.cyan,
|
|
13
|
+
green: chalk.green,
|
|
14
|
+
yellow: chalk.yellow,
|
|
15
|
+
red: chalk.red,
|
|
16
|
+
blue: chalk.blue,
|
|
17
|
+
magenta: chalk.magenta,
|
|
18
|
+
gray: chalk.gray,
|
|
19
|
+
white: chalk.white,
|
|
20
|
+
|
|
21
|
+
// Combinations
|
|
22
|
+
success: chalk.green.bold,
|
|
23
|
+
error: chalk.red.bold,
|
|
24
|
+
warning: chalk.yellow.bold,
|
|
25
|
+
info: chalk.cyan.bold,
|
|
26
|
+
highlight: chalk.cyan,
|
|
27
|
+
muted: chalk.dim,
|
|
28
|
+
|
|
29
|
+
// Symbols
|
|
30
|
+
symbols: {
|
|
31
|
+
success: chalk.green('✔'),
|
|
32
|
+
error: chalk.red('✖'),
|
|
33
|
+
warning: chalk.yellow('⚠'),
|
|
34
|
+
info: chalk.cyan('ℹ'),
|
|
35
|
+
arrow: chalk.cyan('→'),
|
|
36
|
+
bullet: chalk.dim('•')
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// Helper functions
|
|
40
|
+
title: (text) => chalk.cyan.bold(text),
|
|
41
|
+
subtitle: (text) => chalk.dim(text),
|
|
42
|
+
label: (text) => chalk.bold(text),
|
|
43
|
+
value: (text) => chalk.dim(text),
|
|
44
|
+
path: (text) => chalk.dim(text),
|
|
45
|
+
command: (text) => chalk.green(text),
|
|
46
|
+
|
|
47
|
+
// Box drawing
|
|
48
|
+
box: (title) => {
|
|
49
|
+
const line = '─'.repeat(title.length + 4);
|
|
50
|
+
return `${chalk.cyan('┌' + line + '┐')}\n${chalk.cyan('│')} ${chalk.bold(title)} ${chalk.cyan('│')}\n${chalk.cyan('└' + line + '┘')}`;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Banner
|
|
54
|
+
banner: (text) => chalk.cyan.bold(`\n--- ${text} ---\n`)
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
module.exports = style;
|
package/package.json
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wdio-lambdatest-service-sdk",
|
|
3
|
-
"version": "5.
|
|
4
|
-
"description": "WebdriverIO service for LambdaTest
|
|
3
|
+
"version": "5.1.0",
|
|
4
|
+
"description": "WebdriverIO service and CLI for LambdaTest Appium & browser automation",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/LambdaTest/wdio-lambdatest-service"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/LambdaTest/wdio-lambdatest-service#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/LambdaTest/wdio-lambdatest-service/issues"
|
|
13
|
+
},
|
|
6
14
|
"bin": {
|
|
15
|
+
"wdio-lt": "bin/cli.js",
|
|
16
|
+
"wdio-lambdatest": "bin/cli.js",
|
|
7
17
|
"wdio-lambdatest-setup": "bin/setup.js",
|
|
8
18
|
"wdio-lambdatest-generator": "bin/generate-config.js"
|
|
9
19
|
},
|
|
@@ -15,15 +25,25 @@
|
|
|
15
25
|
"wdio",
|
|
16
26
|
"lambdatest",
|
|
17
27
|
"service",
|
|
18
|
-
"sdk"
|
|
28
|
+
"sdk",
|
|
29
|
+
"cli",
|
|
30
|
+
"bun",
|
|
31
|
+
"commander",
|
|
32
|
+
"inquirer"
|
|
19
33
|
],
|
|
20
34
|
"author": "",
|
|
21
35
|
"license": "ISC",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"chalk": "4",
|
|
38
|
+
"commander": "^14.0.2",
|
|
39
|
+
"inquirer": "^13.2.2"
|
|
40
|
+
},
|
|
22
41
|
"peerDependencies": {
|
|
23
42
|
"@wdio/cli": ">=5.0.0",
|
|
24
43
|
"webdriverio": ">=5.0.0"
|
|
25
44
|
},
|
|
26
45
|
"engines": {
|
|
27
|
-
"node": ">=
|
|
46
|
+
"node": ">=18.0.0",
|
|
47
|
+
"bun": ">=1.0.0"
|
|
28
48
|
}
|
|
29
49
|
}
|
package/src/launcher.js
CHANGED
|
@@ -1,3 +1,48 @@
|
|
|
1
|
+
const LT_OPTIONS_KEY = 'lt:options';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolve credentials from service options and env. Prefers options.user/key, then username/accessKey, then env.
|
|
5
|
+
* @param {object} options - Service options (user, key, username, accessKey)
|
|
6
|
+
* @param {object} config - WDIO config (may already have user/key from elsewhere)
|
|
7
|
+
* @returns {{ user: string|undefined, key: string|undefined }}
|
|
8
|
+
*/
|
|
9
|
+
function resolveCredentials(options, config) {
|
|
10
|
+
let user = options.user || options.username || config.user;
|
|
11
|
+
let key = options.key || options.accessKey || config.key;
|
|
12
|
+
if (!user) user = process.env.LT_USERNAME;
|
|
13
|
+
if (!key) key = process.env.LT_ACCESS_KEY;
|
|
14
|
+
return { user, key };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Apply credentials to WDIO config (for Basic Auth).
|
|
19
|
+
* @param {object} config - WDIO config (mutated)
|
|
20
|
+
* @param {{ user: string|undefined, key: string|undefined }} credentials
|
|
21
|
+
*/
|
|
22
|
+
function applyCredentialsToConfig(config, credentials) {
|
|
23
|
+
if (credentials.user) config.user = credentials.user;
|
|
24
|
+
if (credentials.key) config.key = credentials.key;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Apply credentials to each capability (JSONWP and W3C lt:options).
|
|
29
|
+
* @param {Array<object>} capabilities - List of capability objects (mutated)
|
|
30
|
+
* @param {{ user: string|undefined, key: string|undefined }} credentials
|
|
31
|
+
*/
|
|
32
|
+
function applyCredentialsToCapabilities(capabilities, credentials) {
|
|
33
|
+
const { user, key } = credentials;
|
|
34
|
+
if (!user || !key) return;
|
|
35
|
+
|
|
36
|
+
capabilities.forEach((cap) => {
|
|
37
|
+
if (!cap.user) cap.user = user;
|
|
38
|
+
if (!cap.accessKey) cap.accessKey = key;
|
|
39
|
+
const ltOpts = cap[LT_OPTIONS_KEY];
|
|
40
|
+
if (ltOpts) {
|
|
41
|
+
if (!ltOpts.username) ltOpts.username = user;
|
|
42
|
+
if (!ltOpts.accessKey) ltOpts.accessKey = key;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
1
46
|
|
|
2
47
|
module.exports = class LambdaTestLauncher {
|
|
3
48
|
constructor(serviceOptions, capabilities, config) {
|
|
@@ -8,35 +53,13 @@ module.exports = class LambdaTestLauncher {
|
|
|
8
53
|
onPrepare(config, capabilities) {
|
|
9
54
|
console.log('[LambdaTest Service] Processing credentials...');
|
|
10
55
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (!config.user) config.user = process.env.LT_USERNAME;
|
|
19
|
-
if (!config.key) config.key = process.env.LT_ACCESS_KEY;
|
|
20
|
-
|
|
21
|
-
// 2. Update Capabilities (Explicitly pass credentials to caps)
|
|
22
|
-
// This ensures that even if Basic Auth fails or is missed,
|
|
23
|
-
// the grid sees them in the capabilities.
|
|
24
|
-
const user = config.user;
|
|
25
|
-
const key = config.key;
|
|
26
|
-
|
|
27
|
-
if (user && key) {
|
|
28
|
-
const capsList = Array.isArray(capabilities) ? capabilities : [capabilities];
|
|
29
|
-
capsList.forEach(cap => {
|
|
30
|
-
// Legacy JSONWP
|
|
31
|
-
if (!cap.user) cap.user = user;
|
|
32
|
-
if (!cap.accessKey) cap.accessKey = key;
|
|
33
|
-
|
|
34
|
-
// W3C Support (lt:options)
|
|
35
|
-
if (cap['lt:options']) {
|
|
36
|
-
if (!cap['lt:options'].username) cap['lt:options'].username = user;
|
|
37
|
-
if (!cap['lt:options'].accessKey) cap['lt:options'].accessKey = key;
|
|
38
|
-
}
|
|
39
|
-
});
|
|
56
|
+
const credentials = resolveCredentials(this.options, config);
|
|
57
|
+
applyCredentialsToConfig(config, credentials);
|
|
58
|
+
|
|
59
|
+
const capsList = Array.isArray(capabilities) ? capabilities : [capabilities];
|
|
60
|
+
applyCredentialsToCapabilities(capsList, credentials);
|
|
61
|
+
|
|
62
|
+
if (credentials.user && credentials.key) {
|
|
40
63
|
console.log('[LambdaTest Service] Credentials injected into config and capabilities.');
|
|
41
64
|
} else {
|
|
42
65
|
console.error('[LambdaTest Service] Credentials not found! Please check service options or env vars.');
|
package/src/service.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
const LOG_TAG = '[LambdaTest SDK]';
|
|
2
|
+
const COLORS = {
|
|
3
|
+
reset: '\x1b[0m',
|
|
4
|
+
green: '\x1b[32m',
|
|
5
|
+
red: '\x1b[31m',
|
|
6
|
+
cyan: '\x1b[36m',
|
|
7
|
+
gray: '\x1b[90m'
|
|
8
|
+
};
|
|
1
9
|
|
|
10
|
+
/**
|
|
11
|
+
* WebdriverIO service for LambdaTest: updates test status on the LambdaTest platform
|
|
12
|
+
* after each test (Mocha/Jasmine) or scenario (Cucumber).
|
|
13
|
+
*/
|
|
2
14
|
module.exports = class LambdaTestService {
|
|
3
15
|
constructor(serviceOptions, capabilities, config) {
|
|
4
16
|
this.options = serviceOptions || {};
|
|
@@ -10,44 +22,40 @@ module.exports = class LambdaTestService {
|
|
|
10
22
|
}
|
|
11
23
|
|
|
12
24
|
/**
|
|
13
|
-
*
|
|
25
|
+
* WDIO lifecycle: after each test (Mocha/Jasmine). Handles v5/v6/v7 result shape differences.
|
|
14
26
|
*/
|
|
15
27
|
async afterTest(test, context, result) {
|
|
16
|
-
// Handle WDIO v5/v6/v7 differences
|
|
17
28
|
let passed = false;
|
|
18
|
-
let error
|
|
29
|
+
let error;
|
|
19
30
|
let title = 'Test';
|
|
20
31
|
|
|
21
|
-
// Check if result is the object { error, result, duration, passed, retries }
|
|
22
32
|
if (result && typeof result.passed === 'boolean') {
|
|
23
33
|
passed = result.passed;
|
|
24
34
|
error = result.error;
|
|
25
|
-
}
|
|
26
|
-
// Fallback for older/different versions where result might be the passed boolean or different structure
|
|
27
|
-
else if (test && typeof test.passed === 'boolean') {
|
|
35
|
+
} else if (test && typeof test.passed === 'boolean') {
|
|
28
36
|
passed = test.passed;
|
|
29
37
|
error = test.error;
|
|
30
38
|
}
|
|
31
|
-
|
|
32
39
|
if (test && test.title) title = test.title;
|
|
33
40
|
|
|
34
|
-
|
|
41
|
+
const errorMessage = error ? (error.message || error) : undefined;
|
|
42
|
+
await this.updateStatus(passed, errorMessage, title);
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
/**
|
|
38
|
-
*
|
|
46
|
+
* WDIO lifecycle: after each scenario (Cucumber).
|
|
39
47
|
*/
|
|
40
48
|
async afterScenario(uri, feature, scenario, result) {
|
|
41
49
|
const isPassed = result.status === 'passed' || result === 'passed' || result.passed === true;
|
|
42
|
-
const errorMessage = result
|
|
50
|
+
const errorMessage = getScenarioErrorMessage(result, isPassed);
|
|
43
51
|
const title = scenario.name || 'Scenario';
|
|
44
52
|
await this.updateStatus(isPassed, errorMessage, title);
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
/**
|
|
48
|
-
* Updates the test status on LambdaTest
|
|
49
|
-
* @param {boolean} passed
|
|
50
|
-
* @param {string} errorMessage
|
|
56
|
+
* Updates the test status on LambdaTest via browser hook.
|
|
57
|
+
* @param {boolean} passed
|
|
58
|
+
* @param {string|undefined} errorMessage
|
|
51
59
|
* @param {string} testTitle
|
|
52
60
|
*/
|
|
53
61
|
async updateStatus(passed, errorMessage, testTitle) {
|
|
@@ -55,43 +63,31 @@ module.exports = class LambdaTestService {
|
|
|
55
63
|
|
|
56
64
|
const status = passed ? 'passed' : 'failed';
|
|
57
65
|
const remark = errorMessage || (passed ? 'Test Passed' : 'Test Failed');
|
|
58
|
-
|
|
59
|
-
// ANSI Color Codes
|
|
60
|
-
const reset = "\x1b[0m";
|
|
61
|
-
const green = "\x1b[32m";
|
|
62
|
-
const red = "\x1b[31m";
|
|
63
|
-
const cyan = "\x1b[36m";
|
|
64
|
-
const gray = "\x1b[90m";
|
|
65
|
-
|
|
66
|
-
const color = passed ? green : red;
|
|
66
|
+
const color = passed ? COLORS.green : COLORS.red;
|
|
67
67
|
const icon = passed ? '✔' : '✖';
|
|
68
|
-
|
|
69
|
-
console.log(`\n${cyan}
|
|
70
|
-
console.log(`${cyan}
|
|
71
|
-
console.log(`${cyan}
|
|
68
|
+
|
|
69
|
+
console.log(`\n${COLORS.cyan}${LOG_TAG}${COLORS.reset} ${COLORS.gray}Updating Status...${COLORS.reset}`);
|
|
70
|
+
console.log(`${COLORS.cyan}${LOG_TAG}${COLORS.reset} Test: ${testTitle}`);
|
|
71
|
+
console.log(`${COLORS.cyan}${LOG_TAG}${COLORS.reset} Status: ${color}${status.toUpperCase()} ${icon}${COLORS.reset}`);
|
|
72
72
|
if (!passed) {
|
|
73
|
-
console.log(`${cyan}
|
|
73
|
+
console.log(`${COLORS.cyan}${LOG_TAG}${COLORS.reset} Reason: ${COLORS.red}${remark}${COLORS.reset}`);
|
|
74
74
|
}
|
|
75
|
-
console.log('');
|
|
75
|
+
console.log('');
|
|
76
76
|
|
|
77
77
|
try {
|
|
78
|
-
// Using browser.execute to trigger the Lambda Hook
|
|
79
78
|
const script = `lambda-hook: ${JSON.stringify({
|
|
80
79
|
action: 'setTestStatus',
|
|
81
|
-
arguments: {
|
|
82
|
-
status: status,
|
|
83
|
-
remark: remark
|
|
84
|
-
}
|
|
80
|
+
arguments: { status, remark }
|
|
85
81
|
})}`;
|
|
86
|
-
|
|
87
|
-
// Await the execution in case we are in async mode
|
|
88
82
|
await browser.execute(script);
|
|
89
|
-
|
|
90
|
-
// Add a small pause to ensure the hook is processed before session ends
|
|
91
83
|
await browser.pause(1000);
|
|
92
|
-
|
|
93
84
|
} catch (e) {
|
|
94
|
-
console.error(`${red}
|
|
85
|
+
console.error(`${COLORS.red}${LOG_TAG} Failed to update test status via hook.${COLORS.reset}`, e);
|
|
95
86
|
}
|
|
96
87
|
}
|
|
97
88
|
};
|
|
89
|
+
|
|
90
|
+
function getScenarioErrorMessage(result, isPassed) {
|
|
91
|
+
if (result.error) return result.error;
|
|
92
|
+
return isPassed ? undefined : 'Scenario Failed';
|
|
93
|
+
}
|