Compare commits
8 Commits
49270f8d2f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af4b774fea | ||
|
|
9e73900197 | ||
|
|
85537b26bf | ||
|
|
140852be07 | ||
|
|
85f7f81236 | ||
|
|
41e4424650 | ||
|
|
a69911e874 | ||
|
|
fe4ce936c6 |
@@ -37,6 +37,8 @@ The `dump` command creates HTML snapshots in `test-results/Chrome/<dumpname>/` t
|
|||||||
- `break` - Pause execution (press any key to continue)
|
- `break` - Pause execution (press any key to continue)
|
||||||
- `break "waiting for user input"` - Pause with message
|
- `break "waiting for user input"` - Pause with message
|
||||||
- `dump "step_name"` - Take screenshot
|
- `dump "step_name"` - Take screenshot
|
||||||
|
- `jumpIf element=span childText="Server-Warenkorb" jump=4` - Jump over 4 commands if element exists
|
||||||
|
- `jumpIfNot element=span childText="Server-Warenkorb" jump=4` - Jump over 4 commands if element doesn't exist
|
||||||
|
|
||||||
### Element Selectors
|
### Element Selectors
|
||||||
- `element=tagname` - HTML tag (div, span, button, input, a, form, etc.)
|
- `element=tagname` - HTML tag (div, span, button, input, a, form, etc.)
|
||||||
|
|||||||
16
.vscode/extensions/playwrong-syntax/README.md
vendored
16
.vscode/extensions/playwrong-syntax/README.md
vendored
@@ -57,8 +57,24 @@ dump "login_page"
|
|||||||
- Unknown HTML elements
|
- Unknown HTML elements
|
||||||
- Generic element selectors
|
- Generic element selectors
|
||||||
- Semantic misuse (e.g., `click` on input fields)
|
- Semantic misuse (e.g., `click` on input fields)
|
||||||
|
- **Undefined variables**: Variables that are not commonly defined environment variables
|
||||||
|
|
||||||
### Info
|
### Info
|
||||||
- Environment variable usage
|
- Environment variable usage
|
||||||
- Command placement suggestions
|
- Command placement suggestions
|
||||||
- Best practice tips
|
- Best practice tips
|
||||||
|
|
||||||
|
## Variable Validation
|
||||||
|
|
||||||
|
The extension now provides enhanced validation for environment variables:
|
||||||
|
|
||||||
|
- **Common variables** like `$PASSWORD`, `$PASSWORDMAIL`, `$EMAIL`, `$USERNAME`, `$API_KEY`, `$TOKEN`, `$BASE_URL` show as info messages
|
||||||
|
- **Undefined variables** that are not commonly defined show as warnings
|
||||||
|
- Use the CLI with `--strict-variables` flag to treat undefined variables as errors during execution
|
||||||
|
|
||||||
|
## CLI Integration
|
||||||
|
|
||||||
|
The extension works with the PlayWrong CLI which supports:
|
||||||
|
- `--lint` - Run linter before execution
|
||||||
|
- `--strict` - Treat linter warnings as errors
|
||||||
|
- `--strict-variables` - Treat undefined variables as errors during execution
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"name": "keyword.control.playwrong",
|
"name": "keyword.control.playwrong",
|
||||||
"match": "\\b(use|open|wait|click|fill|scroll|sleep|dump|break)\\b"
|
"match": "\\b(use|open|wait|click|fill|scroll|sleep|dump|break|follow|switch to tab|extract)\\b"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "entity.name.function.playwrong",
|
"name": "entity.name.function.playwrong",
|
||||||
|
|||||||
39
README.md
39
README.md
@@ -33,6 +33,31 @@ Run in headless mode (default):
|
|||||||
node src/cli.js tests/example.test Chrome --headless
|
node src/cli.js tests/example.test Chrome --headless
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Variable Validation
|
||||||
|
|
||||||
|
PlayWrong now provides comprehensive validation for environment variables:
|
||||||
|
|
||||||
|
### Linting (Static Analysis)
|
||||||
|
```bash
|
||||||
|
node src/cli.js tests/example.test Chrome --lint
|
||||||
|
```
|
||||||
|
- **Common variables** like `$PASSWORD`, `$PASSWORDMAIL`, `$EMAIL`, `$USERNAME` show as info messages
|
||||||
|
- **Undefined variables** that are not commonly defined show as warnings
|
||||||
|
|
||||||
|
### Runtime Validation
|
||||||
|
During execution, undefined variables:
|
||||||
|
- Show prominent warnings with helpful instructions
|
||||||
|
- Are used as literal text (e.g., `$UNDEFINED_VAR` stays as `$UNDEFINED_VAR`)
|
||||||
|
- Can be treated as errors with `--strict-variables` flag
|
||||||
|
|
||||||
|
### Strict Mode
|
||||||
|
```bash
|
||||||
|
node src/cli.js tests/example.test Chrome --strict-variables
|
||||||
|
```
|
||||||
|
- Treats undefined variables as errors
|
||||||
|
- Stops execution immediately when an undefined variable is encountered
|
||||||
|
- Useful for CI/CD pipelines to ensure all variables are properly set
|
||||||
|
|
||||||
## Test Language Syntax
|
## Test Language Syntax
|
||||||
|
|
||||||
### Profiles
|
### Profiles
|
||||||
@@ -103,6 +128,20 @@ sleep 2500 "waiting for animation"
|
|||||||
sleep 500 "let page settle"
|
sleep 500 "let page settle"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### jumpIf
|
||||||
|
Jump over a specified number of commands if an element exists:
|
||||||
|
```
|
||||||
|
jumpIf element=span childText="Server-Warenkorb" jump=4
|
||||||
|
jumpIf element=div id="error-message" jump=2
|
||||||
|
```
|
||||||
|
|
||||||
|
#### jumpIfNot
|
||||||
|
Jump over a specified number of commands if an element doesn't exist:
|
||||||
|
```
|
||||||
|
jumpIfNot element=span childText="Server-Warenkorb" jump=4
|
||||||
|
jumpIfNot element=div id="success-message" jump=2
|
||||||
|
```
|
||||||
|
|
||||||
### Element Selectors
|
### Element Selectors
|
||||||
|
|
||||||
You can combine multiple attributes:
|
You can combine multiple attributes:
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
use "Chrome"
|
|
||||||
|
|
||||||
# Part 1 - Load Google and accept cookies
|
|
||||||
open "https://google.de"
|
|
||||||
wait element=button childText="Alle akzeptieren"
|
|
||||||
click element=button childText="Alle akzeptieren"
|
|
||||||
sleep 1000 "after cookie accept"
|
|
||||||
dump "after_cookie_accept"
|
|
||||||
|
|
||||||
26
loop-test.bat
Normal file
26
loop-test.bat
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
@echo off
|
||||||
|
echo PlayWrong Test Loop Script
|
||||||
|
echo ========================
|
||||||
|
echo.
|
||||||
|
echo This script will run the PlayWrong test in a loop until it fails.
|
||||||
|
echo Press Ctrl+C to stop the loop manually.
|
||||||
|
echo.
|
||||||
|
|
||||||
|
if "%~1"=="" (
|
||||||
|
echo Usage: loop-test.bat ^<test-file^> [profile] [options]
|
||||||
|
echo.
|
||||||
|
echo Examples:
|
||||||
|
echo loop-test.bat step1.test Chrome
|
||||||
|
echo loop-test.bat step1.test Chrome --delay 1000
|
||||||
|
echo loop-test.bat step1.test Chrome --log my-test.log
|
||||||
|
echo.
|
||||||
|
echo Press any key to exit...
|
||||||
|
pause >nul
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Starting test loop for: %1 with profile: %2
|
||||||
|
echo Log will be saved to a timestamped file
|
||||||
|
echo.
|
||||||
|
|
||||||
|
node loop-test.js %*
|
||||||
204
loop-test.js
Normal file
204
loop-test.js
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const { spawn } = require('child_process');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
class TestLooper {
|
||||||
|
constructor(testFile, profile = 'Chrome', options = {}) {
|
||||||
|
this.testFile = testFile;
|
||||||
|
this.profile = profile;
|
||||||
|
this.options = options;
|
||||||
|
this.runCount = 0;
|
||||||
|
this.successCount = 0;
|
||||||
|
this.failureCount = 0;
|
||||||
|
this.startTime = Date.now();
|
||||||
|
this.logFile = options.logFile || `test-loop-${Date.now()}.log`;
|
||||||
|
}
|
||||||
|
|
||||||
|
log(message) {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const logMessage = `[${timestamp}] ${message}`;
|
||||||
|
console.log(logMessage);
|
||||||
|
|
||||||
|
// Also write to log file
|
||||||
|
fs.appendFileSync(this.logFile, logMessage + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
async runSingleTest() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.runCount++;
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
this.log(`Starting test run #${this.runCount}`);
|
||||||
|
|
||||||
|
const child = spawn('node', ['src/cli.js', this.testFile, this.profile], {
|
||||||
|
stdio: 'pipe',
|
||||||
|
env: process.env
|
||||||
|
});
|
||||||
|
|
||||||
|
let stdout = '';
|
||||||
|
let stderr = '';
|
||||||
|
|
||||||
|
child.stdout.on('data', (data) => {
|
||||||
|
const output = data.toString();
|
||||||
|
stdout += output;
|
||||||
|
// Show real-time progress by forwarding key output lines
|
||||||
|
const lines = output.split('\n').filter(line => line.trim());
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes('Executing:') || line.includes('✓ Completed') || line.includes('Running test file:')) {
|
||||||
|
console.log(` ${line}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
child.stderr.on('data', (data) => {
|
||||||
|
const output = data.toString();
|
||||||
|
stderr += output;
|
||||||
|
// Show errors in real-time
|
||||||
|
const lines = output.split('\n').filter(line => line.trim());
|
||||||
|
for (const line of lines) {
|
||||||
|
console.log(` ERROR: ${line}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', (code) => {
|
||||||
|
const duration = Date.now() - startTime;
|
||||||
|
const minutes = Math.floor(duration / 60000);
|
||||||
|
const seconds = ((duration % 60000) / 1000).toFixed(1);
|
||||||
|
|
||||||
|
if (code === 0) {
|
||||||
|
this.successCount++;
|
||||||
|
this.log(`✅ Test run #${this.runCount} PASSED (${minutes}m ${seconds}s) - Total passed: ${this.successCount}`);
|
||||||
|
resolve({ success: true, code, stdout, stderr, duration });
|
||||||
|
} else {
|
||||||
|
this.failureCount++;
|
||||||
|
this.log(`❌ Test run #${this.runCount} FAILED with exit code ${code} (${minutes}m ${seconds}s) - Total passed: ${this.successCount}`);
|
||||||
|
this.log(`STDOUT: ${stdout}`);
|
||||||
|
this.log(`STDERR: ${stderr}`);
|
||||||
|
resolve({ success: false, code, stdout, stderr, duration });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('error', (error) => {
|
||||||
|
this.failureCount++;
|
||||||
|
this.log(`❌ Test run #${this.runCount} ERROR: ${error.message}`);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
printStatistics() {
|
||||||
|
const totalTime = Date.now() - this.startTime;
|
||||||
|
const totalMinutes = Math.floor(totalTime / 60000);
|
||||||
|
const totalSeconds = ((totalTime % 60000) / 1000).toFixed(1);
|
||||||
|
|
||||||
|
this.log('\n=== FINAL STATISTICS ===');
|
||||||
|
this.log(`Total runs: ${this.runCount}`);
|
||||||
|
this.log(`Successful runs: ${this.successCount}`);
|
||||||
|
this.log(`Failed runs: ${this.failureCount}`);
|
||||||
|
this.log(`Success rate: ${this.runCount > 0 ? ((this.successCount / this.runCount) * 100).toFixed(1) : 0}%`);
|
||||||
|
this.log(`Total time: ${totalMinutes}m ${totalSeconds}s`);
|
||||||
|
this.log(`Average time per run: ${this.runCount > 0 ? (totalTime / this.runCount / 1000).toFixed(1) : 0}s`);
|
||||||
|
this.log(`Log file: ${this.logFile}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async loop() {
|
||||||
|
this.log(`Starting test loop for: ${this.testFile} with profile: ${this.profile}`);
|
||||||
|
this.log(`Log file: ${this.logFile}`);
|
||||||
|
this.log(`Press Ctrl+C to stop the loop\n`);
|
||||||
|
|
||||||
|
// Handle Ctrl+C gracefully
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
this.log('\n\n🛑 Loop interrupted by user');
|
||||||
|
this.printStatistics();
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const result = await this.runSingleTest();
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
this.log('\n🔴 Test failed! Stopping loop...');
|
||||||
|
this.printStatistics();
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: Add a small delay between runs
|
||||||
|
if (this.options.delay) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, this.options.delay));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print periodic statistics
|
||||||
|
if (this.runCount % 10 === 0) {
|
||||||
|
const currentTime = Date.now() - this.startTime;
|
||||||
|
const minutes = Math.floor(currentTime / 60000);
|
||||||
|
const seconds = ((currentTime % 60000) / 1000).toFixed(1);
|
||||||
|
this.log(`📊 Progress: ${this.runCount} runs completed, ${this.successCount} successful (${minutes}m ${seconds}s elapsed)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.log(`💥 Unexpected error: ${error.message}`);
|
||||||
|
this.printStatistics();
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI handling
|
||||||
|
async function main() {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.length === 0) {
|
||||||
|
console.log('Usage: node loop-test.js <test-file> [profile] [options]');
|
||||||
|
console.log('');
|
||||||
|
console.log('Arguments:');
|
||||||
|
console.log(' test-file Path to the test file (e.g., step1.test)');
|
||||||
|
console.log(' profile Browser profile (Chrome, Mobile, MobileSmall) [default: Chrome]');
|
||||||
|
console.log('');
|
||||||
|
console.log('Options:');
|
||||||
|
console.log(' --delay <ms> Add delay between test runs in milliseconds');
|
||||||
|
console.log(' --log <file> Custom log file name');
|
||||||
|
console.log('');
|
||||||
|
console.log('Examples:');
|
||||||
|
console.log(' node loop-test.js step1.test Chrome');
|
||||||
|
console.log(' node loop-test.js step1.test Chrome --delay 1000');
|
||||||
|
console.log(' node loop-test.js step1.test Chrome --log my-test-log.txt');
|
||||||
|
console.log('');
|
||||||
|
console.log('The script will run the test in a loop until it fails.');
|
||||||
|
console.log('Press Ctrl+C to stop the loop manually.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const testFile = args[0];
|
||||||
|
const profile = args[1] || 'Chrome';
|
||||||
|
|
||||||
|
// Parse options
|
||||||
|
const options = {};
|
||||||
|
const delayIndex = args.indexOf('--delay');
|
||||||
|
if (delayIndex !== -1 && args[delayIndex + 1]) {
|
||||||
|
options.delay = parseInt(args[delayIndex + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const logIndex = args.indexOf('--log');
|
||||||
|
if (logIndex !== -1 && args[logIndex + 1]) {
|
||||||
|
options.logFile = args[logIndex + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if test file exists
|
||||||
|
if (!fs.existsSync(testFile)) {
|
||||||
|
console.error(`❌ Test file not found: ${testFile}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const looper = new TestLooper(testFile, profile, options);
|
||||||
|
await looper.loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run if called directly
|
||||||
|
if (require.main === module) {
|
||||||
|
main().catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = TestLooper;
|
||||||
25
loop-test.sh
Normal file
25
loop-test.sh
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "PlayWrong Test Loop Script"
|
||||||
|
echo "========================"
|
||||||
|
echo ""
|
||||||
|
echo "This script will run the PlayWrong test in a loop until it fails."
|
||||||
|
echo "Press Ctrl+C to stop the loop manually."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "Usage: ./loop-test.sh <test-file> [profile] [options]"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " ./loop-test.sh step1.test Chrome"
|
||||||
|
echo " ./loop-test.sh step1.test Chrome --delay 1000"
|
||||||
|
echo " ./loop-test.sh step1.test Chrome --log my-test.log"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting test loop for: $1 with profile: $2"
|
||||||
|
echo "Log will be saved to a timestamped file"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
node loop-test.js "$@"
|
||||||
@@ -4,12 +4,8 @@
|
|||||||
"description": "Custom test language using Playwright",
|
"description": "Custom test language using Playwright",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node src/cli.js step1.test Chrome --headed --screenshot-none",
|
"test": "node src/cli.js step1.test Chrome --headed --screenshot --full-page",
|
||||||
"install-browsers": "playwright install chromium",
|
"install-browsers": "playwright install chromium"
|
||||||
"lint": "node src/linter-cli.js step1.test",
|
|
||||||
"lint:strict": "node src/linter-cli.js --strict step1.test",
|
|
||||||
"lint:verbose": "node src/linter-cli.js --verbose step1.test",
|
|
||||||
"lint:all": "node src/linter-cli.js step1.test"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^17.2.0",
|
"dotenv": "^17.2.0",
|
||||||
|
|||||||
69
src/cli.js
69
src/cli.js
@@ -79,8 +79,47 @@ class TestRunner {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async cleanup() {
|
||||||
|
if (this.executor) {
|
||||||
|
await this.executor.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global reference to runner for cleanup
|
||||||
|
let currentRunner = null;
|
||||||
|
|
||||||
|
// Signal handler for clean shutdown
|
||||||
|
async function handleShutdown(signal) {
|
||||||
|
console.log(`\n🛑 Received ${signal}, shutting down gracefully...`);
|
||||||
|
|
||||||
|
if (currentRunner) {
|
||||||
|
// Signal the executor to stop
|
||||||
|
if (currentRunner.executor) {
|
||||||
|
currentRunner.executor.shouldStop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await currentRunner.cleanup();
|
||||||
|
console.log('✅ Browser cleanup completed');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('⚠️ Browser cleanup had issues:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('👋 Goodbye!');
|
||||||
|
|
||||||
|
// Give a moment for any remaining cleanup, then exit
|
||||||
|
setTimeout(() => {
|
||||||
|
process.exit(0);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register signal handlers
|
||||||
|
process.on('SIGINT', handleShutdown); // Ctrl+C
|
||||||
|
process.on('SIGTERM', handleShutdown); // Termination signal
|
||||||
|
|
||||||
// CLI handling
|
// CLI handling
|
||||||
async function main() {
|
async function main() {
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
@@ -92,13 +131,17 @@ async function main() {
|
|||||||
console.log(' --headed Run in headed mode (show browser)');
|
console.log(' --headed Run in headed mode (show browser)');
|
||||||
console.log(' --headless Run in headless mode (default)');
|
console.log(' --headless Run in headless mode (default)');
|
||||||
console.log(' --enable-animations Enable CSS animations (default: disabled)');
|
console.log(' --enable-animations Enable CSS animations (default: disabled)');
|
||||||
|
console.log(' --full-page Take full page screenshots instead of viewport only');
|
||||||
console.log(' --lint Run linter before execution');
|
console.log(' --lint Run linter before execution');
|
||||||
console.log(' --strict Treat linter warnings as errors');
|
console.log(' --strict Treat linter warnings as errors');
|
||||||
|
console.log(' --strict-variables Treat undefined variables as errors');
|
||||||
console.log('Examples:');
|
console.log('Examples:');
|
||||||
console.log(' node src/cli.js tests/example.test Chrome');
|
console.log(' node src/cli.js tests/example.test Chrome');
|
||||||
console.log(' node src/cli.js tests/example.test Chrome --headed');
|
console.log(' node src/cli.js tests/example.test Chrome --headed');
|
||||||
console.log(' node src/cli.js tests/example.test Chrome --headed --enable-animations');
|
console.log(' node src/cli.js tests/example.test Chrome --headed --enable-animations');
|
||||||
|
console.log(' node src/cli.js tests/example.test Chrome --headed --full-page');
|
||||||
console.log(' node src/cli.js tests/example.test Chrome --lint --strict');
|
console.log(' node src/cli.js tests/example.test Chrome --lint --strict');
|
||||||
|
console.log(' node src/cli.js tests/example.test Chrome --strict-variables');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,21 +149,43 @@ async function main() {
|
|||||||
const profile = args[1] || 'Chrome';
|
const profile = args[1] || 'Chrome';
|
||||||
const headless = !args.includes('--headed');
|
const headless = !args.includes('--headed');
|
||||||
const disableAnimations = !args.includes('--enable-animations');
|
const disableAnimations = !args.includes('--enable-animations');
|
||||||
|
const fullPageScreenshots = args.includes('--full-page');
|
||||||
const lint = args.includes('--lint');
|
const lint = args.includes('--lint');
|
||||||
const strict = args.includes('--strict');
|
const strict = args.includes('--strict');
|
||||||
|
const strictVariables = args.includes('--strict-variables');
|
||||||
|
|
||||||
if (!await fs.pathExists(testFile)) {
|
if (!await fs.pathExists(testFile)) {
|
||||||
console.error(`Test file not found: ${testFile}`);
|
console.error(`Test file not found: ${testFile}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const runner = new TestRunner({ headless, disableAnimations, lint, strict });
|
const runner = new TestRunner({ headless, disableAnimations, fullPageScreenshots, lint, strict, strictVariables });
|
||||||
|
currentRunner = runner; // Store reference for cleanup
|
||||||
|
|
||||||
|
try {
|
||||||
await runner.runTestFile(testFile, profile);
|
await runner.runTestFile(testFile, profile);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Test execution failed:', error.message);
|
||||||
|
|
||||||
|
// Clean up on error
|
||||||
|
try {
|
||||||
|
await runner.cleanup();
|
||||||
|
} catch (cleanupError) {
|
||||||
|
// Ignore cleanup errors
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
currentRunner = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run if called directly
|
// Run if called directly
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
main().catch(console.error);
|
main().catch((error) => {
|
||||||
|
console.error('❌ Unexpected error:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = TestRunner;
|
module.exports = TestRunner;
|
||||||
1011
src/executor.js
1011
src/executor.js
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ class TestLinter {
|
|||||||
this.info = [];
|
this.info = [];
|
||||||
|
|
||||||
// Valid commands
|
// Valid commands
|
||||||
this.validCommands = ['use', 'open', 'wait', 'click', 'scroll', 'fill', 'break', 'sleep', 'dump'];
|
this.validCommands = ['use', 'open', 'wait', 'click', 'scroll', 'fill', 'break', 'sleep', 'dump', 'follow', 'switchToTab', 'extract', 'jumpIf', 'jumpIfNot'];
|
||||||
|
|
||||||
// Valid HTML elements
|
// Valid HTML elements
|
||||||
this.validElements = [
|
this.validElements = [
|
||||||
@@ -29,7 +29,12 @@ class TestLinter {
|
|||||||
'fill': ['element', 'value'],
|
'fill': ['element', 'value'],
|
||||||
'break': [],
|
'break': [],
|
||||||
'sleep': ['milliseconds'],
|
'sleep': ['milliseconds'],
|
||||||
'dump': ['name']
|
'dump': ['name'],
|
||||||
|
'follow': [],
|
||||||
|
'switchToTab': ['tabIndex'],
|
||||||
|
'extract': ['element', 'attribute'],
|
||||||
|
'jumpIf': ['element', 'jump'],
|
||||||
|
'jumpIfNot': ['element', 'jump']
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize rules
|
// Initialize rules
|
||||||
@@ -252,6 +257,27 @@ class TestLinter {
|
|||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const command = line.cleaned.split(' ')[0];
|
const command = line.cleaned.split(' ')[0];
|
||||||
|
|
||||||
|
// Handle multi-word commands
|
||||||
|
if (line.cleaned.startsWith('follow')) {
|
||||||
|
continue; // This is valid
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.cleaned.startsWith('switch to tab')) {
|
||||||
|
continue; // This is valid
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.cleaned.startsWith('extract ')) {
|
||||||
|
continue; // This is valid
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.cleaned.startsWith('jumpIf ')) {
|
||||||
|
continue; // This is valid
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.cleaned.startsWith('jumpIfNot ')) {
|
||||||
|
continue; // This is valid
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.validCommands.includes(command)) {
|
if (!this.validCommands.includes(command)) {
|
||||||
this.addError(`Invalid command '${command}'. Valid commands: ${this.validCommands.join(', ')}`, line.lineNumber);
|
this.addError(`Invalid command '${command}'. Valid commands: ${this.validCommands.join(', ')}`, line.lineNumber);
|
||||||
}
|
}
|
||||||
@@ -312,6 +338,16 @@ class TestLinter {
|
|||||||
if (!line.cleaned.match(/\d+/)) {
|
if (!line.cleaned.match(/\d+/)) {
|
||||||
this.addError(`Command '${command}' requires numeric milliseconds`, line.lineNumber);
|
this.addError(`Command '${command}' requires numeric milliseconds`, line.lineNumber);
|
||||||
}
|
}
|
||||||
|
} else if (reqParam === 'tabIndex' && command === 'switchToTab') {
|
||||||
|
if (!line.cleaned.match(/\d+/)) {
|
||||||
|
this.addError(`Command '${command}' requires numeric tabIndex`, line.lineNumber);
|
||||||
|
}
|
||||||
|
} else if (reqParam === 'attribute' && command === 'extract') {
|
||||||
|
// Special handling for extract command - attribute is part of command syntax
|
||||||
|
const extractMatch = line.cleaned.match(/extract\s+(\w+)\s+from\s+(.+?)\s+to\s+"([^"]+)"/);
|
||||||
|
if (!extractMatch) {
|
||||||
|
this.addError(`Command '${command}' has invalid syntax. Expected: extract <attribute> from <selector> to "<variable>"`, line.lineNumber);
|
||||||
|
}
|
||||||
} else if (!params[reqParam]) {
|
} else if (!params[reqParam]) {
|
||||||
this.addError(`Command '${command}' missing required parameter '${reqParam}'`, line.lineNumber);
|
this.addError(`Command '${command}' missing required parameter '${reqParam}'`, line.lineNumber);
|
||||||
}
|
}
|
||||||
@@ -347,9 +383,14 @@ class TestLinter {
|
|||||||
this.addWarning(`Variable '${variable}' should use UPPER_CASE naming convention`, line.lineNumber);
|
this.addWarning(`Variable '${variable}' should use UPPER_CASE naming convention`, line.lineNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info about common variables
|
// Check if variable is commonly defined
|
||||||
if (['$PASSWORD', '$PASSWORDMAIL', '$EMAIL', '$USERNAME'].includes(variable)) {
|
const commonVariables = ['$PASSWORD', '$PASSWORDMAIL', '$EMAIL', '$USERNAME', '$API_KEY', '$TOKEN', '$BASE_URL'];
|
||||||
|
|
||||||
|
if (commonVariables.includes(variable)) {
|
||||||
this.addInfo(`Using environment variable '${variable}' - ensure it's defined in your environment`, line.lineNumber);
|
this.addInfo(`Using environment variable '${variable}' - ensure it's defined in your environment`, line.lineNumber);
|
||||||
|
} else {
|
||||||
|
// Warn about potentially undefined variables
|
||||||
|
this.addWarning(`Variable '${variable}' may not be defined. Ensure it's set as an environment variable or stored variable`, line.lineNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,23 +400,33 @@ class TestLinter {
|
|||||||
validateFlowLogic(lines) {
|
validateFlowLogic(lines) {
|
||||||
let hasUseCommand = false;
|
let hasUseCommand = false;
|
||||||
let useLineNumber = 0;
|
let useLineNumber = 0;
|
||||||
|
let firstCommandLineIndex = -1;
|
||||||
|
|
||||||
for (const line of lines) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
const command = line.cleaned.split(' ')[0];
|
const command = line.cleaned.split(' ')[0];
|
||||||
|
|
||||||
|
// Track the first command line (index in the cleaned lines array)
|
||||||
|
if (firstCommandLineIndex === -1) {
|
||||||
|
firstCommandLineIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
if (command === 'use') {
|
if (command === 'use') {
|
||||||
if (hasUseCommand) {
|
if (hasUseCommand) {
|
||||||
this.addWarning(`Multiple 'use' commands found. Consider using multi-profile syntax instead.`, line.lineNumber);
|
this.addWarning(`Multiple 'use' commands found. Consider using multi-profile syntax instead.`, line.lineNumber);
|
||||||
}
|
}
|
||||||
hasUseCommand = true;
|
hasUseCommand = true;
|
||||||
useLineNumber = line.lineNumber;
|
useLineNumber = line.lineNumber;
|
||||||
|
|
||||||
|
// Check if this is NOT the first command in the cleaned lines
|
||||||
|
if (i > 0) {
|
||||||
|
this.addInfo(`'use' command found at line ${useLineNumber}. Consider placing it at the beginning of the test.`, useLineNumber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasUseCommand) {
|
if (!hasUseCommand) {
|
||||||
this.addWarning(`No 'use' command found. Tests should specify browser profile.`, 1);
|
this.addWarning(`No 'use' command found. Tests should specify browser profile.`, 1);
|
||||||
} else if (useLineNumber > 1) {
|
|
||||||
this.addInfo(`'use' command found at line ${useLineNumber}. Consider placing it at the beginning of the test.`, useLineNumber);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
106
src/parser.js
106
src/parser.js
@@ -107,6 +107,8 @@ class TestParser {
|
|||||||
class: params.class,
|
class: params.class,
|
||||||
href: params.href,
|
href: params.href,
|
||||||
htmlType: params.type, // Store HTML type as htmlType to avoid conflict
|
htmlType: params.type, // Store HTML type as htmlType to avoid conflict
|
||||||
|
for: params.for,
|
||||||
|
ariaLabel: params['aria-label'],
|
||||||
child: params.child,
|
child: params.child,
|
||||||
childText: params.childText
|
childText: params.childText
|
||||||
};
|
};
|
||||||
@@ -124,6 +126,8 @@ class TestParser {
|
|||||||
class: params.class,
|
class: params.class,
|
||||||
href: params.href,
|
href: params.href,
|
||||||
htmlType: params.type, // Store HTML type as htmlType to avoid conflict
|
htmlType: params.type, // Store HTML type as htmlType to avoid conflict
|
||||||
|
for: params.for,
|
||||||
|
ariaLabel: params['aria-label'],
|
||||||
child: params.child,
|
child: params.child,
|
||||||
childText: params.childText
|
childText: params.childText
|
||||||
};
|
};
|
||||||
@@ -141,6 +145,8 @@ class TestParser {
|
|||||||
class: params.class,
|
class: params.class,
|
||||||
href: params.href,
|
href: params.href,
|
||||||
htmlType: params.type, // Store HTML type as htmlType to avoid conflict
|
htmlType: params.type, // Store HTML type as htmlType to avoid conflict
|
||||||
|
for: params.for,
|
||||||
|
ariaLabel: params['aria-label'],
|
||||||
child: params.child,
|
child: params.child,
|
||||||
childText: params.childText
|
childText: params.childText
|
||||||
};
|
};
|
||||||
@@ -159,6 +165,8 @@ class TestParser {
|
|||||||
class: params.class,
|
class: params.class,
|
||||||
href: params.href,
|
href: params.href,
|
||||||
htmlType: params.type, // Store HTML type as htmlType to avoid conflict
|
htmlType: params.type, // Store HTML type as htmlType to avoid conflict
|
||||||
|
for: params.for,
|
||||||
|
ariaLabel: params['aria-label'],
|
||||||
child: params.child,
|
child: params.child,
|
||||||
childText: params.childText
|
childText: params.childText
|
||||||
};
|
};
|
||||||
@@ -186,6 +194,102 @@ class TestParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse follow command (previously switch to new window)
|
||||||
|
if (line.startsWith('follow')) {
|
||||||
|
return {
|
||||||
|
type: 'follow'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse jumpIf command: jumpIf element=span childText="Server-Warenkorb" jump=4
|
||||||
|
if (line.startsWith('jumpIf ')) {
|
||||||
|
const jumpMatch = line.match(/jump=(\d+)/);
|
||||||
|
if (jumpMatch) {
|
||||||
|
const jumpCount = parseInt(jumpMatch[1]);
|
||||||
|
const selectorPart = line.substring(7).replace(/\s+jump=\d+/, ''); // Remove 'jumpIf ' and jump=X
|
||||||
|
const params = this.parseParameters(selectorPart);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'jumpIf',
|
||||||
|
jumpCount: jumpCount,
|
||||||
|
element: params.element,
|
||||||
|
name: params.name,
|
||||||
|
id: params.id,
|
||||||
|
class: params.class,
|
||||||
|
href: params.href,
|
||||||
|
htmlType: params.type,
|
||||||
|
for: params.for,
|
||||||
|
ariaLabel: params['aria-label'],
|
||||||
|
child: params.child,
|
||||||
|
childText: params.childText
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse jumpIfNot command: jumpIfNot element=span childText="Server-Warenkorb" jump=4
|
||||||
|
if (line.startsWith('jumpIfNot ')) {
|
||||||
|
const jumpMatch = line.match(/jump=(\d+)/);
|
||||||
|
if (jumpMatch) {
|
||||||
|
const jumpCount = parseInt(jumpMatch[1]);
|
||||||
|
const selectorPart = line.substring(10).replace(/\s+jump=\d+/, ''); // Remove 'jumpIfNot ' and jump=X
|
||||||
|
const params = this.parseParameters(selectorPart);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'jumpIfNot',
|
||||||
|
jumpCount: jumpCount,
|
||||||
|
element: params.element,
|
||||||
|
name: params.name,
|
||||||
|
id: params.id,
|
||||||
|
class: params.class,
|
||||||
|
href: params.href,
|
||||||
|
htmlType: params.type,
|
||||||
|
for: params.for,
|
||||||
|
ariaLabel: params['aria-label'],
|
||||||
|
child: params.child,
|
||||||
|
childText: params.childText
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse switch to tab command: switch to tab 0
|
||||||
|
if (line.startsWith('switch to tab ')) {
|
||||||
|
const match = line.match(/switch to tab (\d+)/);
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
type: 'switchToTab',
|
||||||
|
tabIndex: parseInt(match[1])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse extract command: extract href from element=a childText="text" to "ORDER_URL"
|
||||||
|
if (line.startsWith('extract ')) {
|
||||||
|
const match = line.match(/extract\s+(\w+)\s+from\s+(.+?)\s+to\s+"([^"]+)"/);
|
||||||
|
if (match) {
|
||||||
|
const attribute = match[1];
|
||||||
|
const selectorPart = match[2];
|
||||||
|
const variableName = match[3];
|
||||||
|
const params = this.parseParameters(selectorPart);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'extract',
|
||||||
|
attribute: attribute,
|
||||||
|
variableName: variableName,
|
||||||
|
element: params.element,
|
||||||
|
name: params.name,
|
||||||
|
id: params.id,
|
||||||
|
class: params.class,
|
||||||
|
href: params.href,
|
||||||
|
htmlType: params.type,
|
||||||
|
for: params.for,
|
||||||
|
ariaLabel: params['aria-label'],
|
||||||
|
child: params.child,
|
||||||
|
childText: params.childText,
|
||||||
|
target: params.target
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +337,7 @@ class TestParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseSimpleParameters(paramString, params) {
|
parseSimpleParameters(paramString, params) {
|
||||||
const regex = /(\w+)=(?:"([^"]*)"|([^\s]+))/g;
|
const regex = /([\w-]+)=(?:"([^"]*)"|([^\s]+))/g;
|
||||||
let match;
|
let match;
|
||||||
|
|
||||||
while ((match = regex.exec(paramString)) !== null) {
|
while ((match = regex.exec(paramString)) !== null) {
|
||||||
|
|||||||
58
step1.test
58
step1.test
@@ -9,71 +9,65 @@ Part 3: Login to the email account
|
|||||||
use "Chrome"
|
use "Chrome"
|
||||||
|
|
||||||
# Part 1 - Load Growheads, put one item in the cart and go to checkout
|
# Part 1 - Load Growheads, put one item in the cart and go to checkout
|
||||||
open "https://growheads.de"
|
open "https://dev.seedheads.de"
|
||||||
sleep 2000 "page load"
|
sleep 200 "page load"
|
||||||
wait element=a href=/Kategorie/Seeds
|
wait element=a href=/Kategorie/Seeds
|
||||||
click element=a href=/Kategorie/Seeds
|
click element=a href=/Kategorie/Seeds
|
||||||
sleep 2000 "seed click"
|
sleep 200 "seed click"
|
||||||
wait element=button childText="In den Korb"
|
wait element=button childText="In den Korb"
|
||||||
click element=button childText="In den Korb"
|
click element=button childText="In den Korb"
|
||||||
sleep 2000 "in korb click"
|
sleep 200 "in korb click"
|
||||||
wait element=span class="MuiBadge-badge" childText="1"
|
wait element=span class="MuiBadge-badge" childText="1"
|
||||||
click element=button child=span(class="MuiBadge-badge" childText="1")
|
click element=button child=span(class="MuiBadge-badge" childText="1")
|
||||||
sleep 2000 "korb click"
|
sleep 200 "korb click"
|
||||||
wait element=button childText="Weiter zur Kasse"
|
wait element=button childText="Weiter zur Kasse"
|
||||||
click element=button childText="Weiter zur Kasse"
|
click element=button childText="Weiter zur Kasse"
|
||||||
sleep 2000 "weiter click"
|
sleep 200 "weiter click"
|
||||||
wait element=input type="email"
|
wait element=input type="email"
|
||||||
fill element=input type="email" value="autotest@growheads.de"
|
fill element=input type="email" value="autotest@growheads.de"
|
||||||
sleep 2000 "email fill"
|
sleep 200 "email fill"
|
||||||
wait element=input type="password"
|
wait element=input type="password"
|
||||||
fill element=input type="password" value="$PASSWORD"
|
fill element=input type="password" value="$PASSWORD"
|
||||||
sleep 2000 "password fill"
|
sleep 200 "password fill"
|
||||||
wait element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
wait element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
click element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
click element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
sleep 3000 "anmelden click"
|
sleep 2000 "anmelden click"
|
||||||
|
#dump "isServer"
|
||||||
|
jumpIfNot element=span childText="Server-Warenkorb löschen" jump=2
|
||||||
|
click element=button childText="Weiter"
|
||||||
|
sleep 2000 "anmelden click"
|
||||||
|
|
||||||
# Part 2 - Fill in the checkout form
|
# Part 2 - Fill in the checkout form
|
||||||
scroll element=span childText="Vorname"
|
|
||||||
wait element=input name="firstName"
|
wait element=input name="firstName"
|
||||||
|
scroll element=span childText="Vorname"
|
||||||
fill element=input name="firstName" value="Max"
|
fill element=input name="firstName" value="Max"
|
||||||
sleep 1000 "vorname fill"
|
sleep 100 "vorname fill"
|
||||||
wait element=input name="lastName"
|
wait element=input name="lastName"
|
||||||
fill element=input name="lastName" value="Muster"
|
fill element=input name="lastName" value="Muster"
|
||||||
sleep 1000 "nachname fill"
|
sleep 100 "nachname fill"
|
||||||
wait element=input name="street"
|
wait element=input name="street"
|
||||||
fill element=input name="street" value="Muster"
|
fill element=input name="street" value="Muster"
|
||||||
sleep 1000 "strasse fill"
|
sleep 100 "strasse fill"
|
||||||
wait element=input name="houseNumber"
|
wait element=input name="houseNumber"
|
||||||
fill element=input name="houseNumber" value="420"
|
fill element=input name="houseNumber" value="420"
|
||||||
sleep 1000 "hausnummer fill"
|
sleep 100 "hausnummer fill"
|
||||||
wait element=input name="postalCode"
|
wait element=input name="postalCode"
|
||||||
fill element=input name="postalCode" value="42023"
|
fill element=input name="postalCode" value="42023"
|
||||||
sleep 1000 "plz fill"
|
sleep 100 "plz fill"
|
||||||
wait element=input name="city"
|
wait element=input name="city"
|
||||||
fill element=input name="city" value="Muster"
|
fill element=input name="city" value="Muster"
|
||||||
sleep 1000 "stadt fill"
|
sleep 100 "stadt fill"
|
||||||
|
|
||||||
wait element=textarea name="note"
|
wait element=textarea name="note"
|
||||||
|
scroll element=textarea name="note"
|
||||||
fill element=textarea name="note" value="Musteranmerkung"
|
fill element=textarea name="note" value="Musteranmerkung"
|
||||||
sleep 1000 "note fill"
|
sleep 100 "note fill"
|
||||||
scroll element=button childText="Bestellung abschließen"
|
scroll element=button childText="Bestellung abschließen"
|
||||||
wait element=label childText="Bestimmungen"
|
wait element=label childText="Bestimmungen"
|
||||||
click element=label childText="Bestimmungen"
|
click element=label childText="Bestimmungen"
|
||||||
sleep 1000 "checkbox checked"
|
sleep 100 "checkbox checked"
|
||||||
wait element=button childText="Bestellung abschließen"
|
wait element=button childText="Bestellung abschließen"
|
||||||
#click element=button childText="Bestellung abschließen"
|
click element=button childText="Bestellung abschließen"
|
||||||
sleep 3000 "order completion"
|
sleep 3000 "order completion"
|
||||||
|
|
||||||
# Part 3 - Login to the email account
|
|
||||||
open "https://mail.growbnb.de/"
|
|
||||||
sleep 2000 "page load"
|
|
||||||
wait element=input name="_user" id="rcmloginuser"
|
|
||||||
fill element=input name="_user" id="rcmloginuser" value="autotest@growheads.de"
|
|
||||||
sleep 1000 "username fill"
|
|
||||||
wait element=input name="_pass" id="rcmloginpwd"
|
|
||||||
fill element=input name="_pass" id="rcmloginpwd" value="$PASSWORDMAIL"
|
|
||||||
sleep 1000 "password fill"
|
|
||||||
wait element=button type="submit" id="rcmloginsubmit"
|
|
||||||
click element=button type="submit" id="rcmloginsubmit"
|
|
||||||
sleep 3000 "login submit"
|
|
||||||
dump "email_logged_in"
|
|
||||||
77
step1_dhl.test
Normal file
77
step1_dhl.test
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
PlayWrong Test - Complete E-commerce Flow
|
||||||
|
This test demonstrates the full purchase flow from product selection to order completion
|
||||||
|
with beautiful animations and environment variable support
|
||||||
|
Part 1: Load Growheads, put one item in the cart and go to checkout
|
||||||
|
Part 2: Fill in the checkout form
|
||||||
|
Part 3: Login to the email account
|
||||||
|
*/
|
||||||
|
use "Chrome"
|
||||||
|
|
||||||
|
# Part 1 - Load Growheads, put one item in the cart and go to checkout
|
||||||
|
open "https://dev.seedheads.de"
|
||||||
|
sleep 200 "page load"
|
||||||
|
wait element=a href=/Kategorie/Seeds
|
||||||
|
click element=a href=/Kategorie/Seeds
|
||||||
|
sleep 200 "seed click"
|
||||||
|
wait element=button childText="In den Korb"
|
||||||
|
click element=button childText="In den Korb"
|
||||||
|
sleep 200 "in korb click"
|
||||||
|
wait element=span class="MuiBadge-badge" childText="1"
|
||||||
|
click element=button child=span(class="MuiBadge-badge" childText="1")
|
||||||
|
sleep 200 "korb click"
|
||||||
|
wait element=button childText="Weiter zur Kasse"
|
||||||
|
click element=button childText="Weiter zur Kasse"
|
||||||
|
sleep 200 "weiter click"
|
||||||
|
wait element=input type="email"
|
||||||
|
fill element=input type="email" value="autotest@growheads.de"
|
||||||
|
sleep 200 "email fill"
|
||||||
|
wait element=input type="password"
|
||||||
|
fill element=input type="password" value="$PASSWORD"
|
||||||
|
sleep 200 "password fill"
|
||||||
|
wait element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
|
click element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
|
sleep 2000 "anmelden click"
|
||||||
|
#dump "isServer"
|
||||||
|
jumpIfNot element=span childText="Server-Warenkorb löschen" jump=2
|
||||||
|
click element=button childText="Weiter"
|
||||||
|
sleep 2000 "anmelden click"
|
||||||
|
|
||||||
|
# Part 2 - Fill in the checkout form
|
||||||
|
|
||||||
|
wait element=input name="firstName"
|
||||||
|
scroll element=span childText="Vorname"
|
||||||
|
fill element=input name="firstName" value="Max"
|
||||||
|
sleep 100 "vorname fill"
|
||||||
|
wait element=input name="lastName"
|
||||||
|
fill element=input name="lastName" value="Muster"
|
||||||
|
sleep 100 "nachname fill"
|
||||||
|
wait element=input name="street"
|
||||||
|
fill element=input name="street" value="Muster"
|
||||||
|
sleep 100 "strasse fill"
|
||||||
|
wait element=input name="houseNumber"
|
||||||
|
fill element=input name="houseNumber" value="420"
|
||||||
|
sleep 100 "hausnummer fill"
|
||||||
|
wait element=input name="postalCode"
|
||||||
|
fill element=input name="postalCode" value="42023"
|
||||||
|
sleep 100 "plz fill"
|
||||||
|
wait element=input name="city"
|
||||||
|
fill element=input name="city" value="Muster"
|
||||||
|
sleep 100 "stadt fill"
|
||||||
|
|
||||||
|
wait element=textarea name="note"
|
||||||
|
scroll element=textarea name="note"
|
||||||
|
fill element=textarea name="note" value="Musteranmerkung"
|
||||||
|
sleep 100 "note fill"
|
||||||
|
wait element=input id="DHL" name="deliveryMethod" value="DHL" type="radio"
|
||||||
|
scroll element=input id="DHL" name="deliveryMethod" value="DHL" type="radio"
|
||||||
|
click element=input id="DHL" name="deliveryMethod" value="DHL" type="radio"
|
||||||
|
sleep 100 "dhl click"
|
||||||
|
wait element=label childText="Bestimmungen"
|
||||||
|
scroll element=button childText="Bestellung abschließen"
|
||||||
|
click element=label childText="Bestimmungen"
|
||||||
|
sleep 100 "checkbox checked"
|
||||||
|
wait element=button childText="Bestellung abschließen"
|
||||||
|
click element=button childText="Bestellung abschließen"
|
||||||
|
sleep 3000 "order completion"
|
||||||
|
|
||||||
80
step1_dhl_nach.test
Normal file
80
step1_dhl_nach.test
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
PlayWrong Test - Complete E-commerce Flow
|
||||||
|
This test demonstrates the full purchase flow from product selection to order completion
|
||||||
|
with beautiful animations and environment variable support
|
||||||
|
Part 1: Load Growheads, put one item in the cart and go to checkout
|
||||||
|
Part 2: Fill in the checkout form
|
||||||
|
Part 3: Login to the email account
|
||||||
|
*/
|
||||||
|
use "Chrome"
|
||||||
|
|
||||||
|
# Part 1 - Load Growheads, put one item in the cart and go to checkout
|
||||||
|
open "https://dev.seedheads.de"
|
||||||
|
sleep 200 "page load"
|
||||||
|
wait element=a href=/Kategorie/Seeds
|
||||||
|
click element=a href=/Kategorie/Seeds
|
||||||
|
sleep 200 "seed click"
|
||||||
|
wait element=button childText="In den Korb"
|
||||||
|
click element=button childText="In den Korb"
|
||||||
|
sleep 200 "in korb click"
|
||||||
|
wait element=span class="MuiBadge-badge" childText="1"
|
||||||
|
click element=button child=span(class="MuiBadge-badge" childText="1")
|
||||||
|
sleep 200 "korb click"
|
||||||
|
wait element=button childText="Weiter zur Kasse"
|
||||||
|
click element=button childText="Weiter zur Kasse"
|
||||||
|
sleep 200 "weiter click"
|
||||||
|
wait element=input type="email"
|
||||||
|
fill element=input type="email" value="autotest@growheads.de"
|
||||||
|
sleep 200 "email fill"
|
||||||
|
wait element=input type="password"
|
||||||
|
fill element=input type="password" value="$PASSWORD"
|
||||||
|
sleep 200 "password fill"
|
||||||
|
wait element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
|
click element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
|
sleep 2000 "anmelden click"
|
||||||
|
#dump "isServer"
|
||||||
|
jumpIfNot element=span childText="Server-Warenkorb löschen" jump=2
|
||||||
|
click element=button childText="Weiter"
|
||||||
|
sleep 2000 "anmelden click"
|
||||||
|
|
||||||
|
# Part 2 - Fill in the checkout form
|
||||||
|
|
||||||
|
wait element=input name="firstName"
|
||||||
|
scroll element=span childText="Vorname"
|
||||||
|
fill element=input name="firstName" value="Max"
|
||||||
|
sleep 100 "vorname fill"
|
||||||
|
wait element=input name="lastName"
|
||||||
|
fill element=input name="lastName" value="Muster"
|
||||||
|
sleep 100 "nachname fill"
|
||||||
|
wait element=input name="street"
|
||||||
|
fill element=input name="street" value="Muster"
|
||||||
|
sleep 100 "strasse fill"
|
||||||
|
wait element=input name="houseNumber"
|
||||||
|
fill element=input name="houseNumber" value="420"
|
||||||
|
sleep 100 "hausnummer fill"
|
||||||
|
wait element=input name="postalCode"
|
||||||
|
fill element=input name="postalCode" value="42023"
|
||||||
|
sleep 100 "plz fill"
|
||||||
|
wait element=input name="city"
|
||||||
|
fill element=input name="city" value="Muster"
|
||||||
|
sleep 100 "stadt fill"
|
||||||
|
|
||||||
|
wait element=textarea name="note"
|
||||||
|
scroll element=textarea name="note"
|
||||||
|
fill element=textarea name="note" value="Musteranmerkung"
|
||||||
|
sleep 100 "note fill"
|
||||||
|
wait element=input id="DHL" name="deliveryMethod" value="DHL" type="radio"
|
||||||
|
scroll element=input id="DHL" name="deliveryMethod" value="DHL" type="radio"
|
||||||
|
click element=input id="DHL" name="deliveryMethod" value="DHL" type="radio"
|
||||||
|
sleep 100 "dhl click"
|
||||||
|
wait element=input id="onDelivery" name="paymentMethod" value="onDelivery" type="radio"
|
||||||
|
scroll element=input id="onDelivery" name="paymentMethod" value="onDelivery" type="radio"
|
||||||
|
click element=input id="onDelivery" name="paymentMethod" value="onDelivery" type="radio"
|
||||||
|
sleep 100 "on delivery click"
|
||||||
|
scroll element=button childText="Bestellung abschließen"
|
||||||
|
click element=label childText="Bestimmungen"
|
||||||
|
sleep 100 "checkbox checked"
|
||||||
|
wait element=button childText="Bestellung abschließen"
|
||||||
|
click element=button childText="Bestellung abschließen"
|
||||||
|
sleep 3000 "order completion"
|
||||||
|
|
||||||
96
step1_dhl_nach2.test
Normal file
96
step1_dhl_nach2.test
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
PlayWrong Test - Complete E-commerce Flow
|
||||||
|
This test demonstrates the full purchase flow from product selection to order completion
|
||||||
|
with beautiful animations and environment variable support
|
||||||
|
Part 1: Load Growheads, put one item in the cart and go to checkout
|
||||||
|
Part 2: Fill in the checkout form
|
||||||
|
Part 3: Login to the email account
|
||||||
|
|
||||||
|
node src/cli.js step1_dhl_nach2.test Chrome --headed
|
||||||
|
*/
|
||||||
|
use "Chrome"
|
||||||
|
|
||||||
|
# Part 1 - Load Growheads, put one item in the cart and go to checkout
|
||||||
|
open "https://dev.seedheads.de"
|
||||||
|
sleep 200 "page load"
|
||||||
|
wait element=a href=/Kategorie/Seeds
|
||||||
|
click element=a href=/Kategorie/Seeds
|
||||||
|
sleep 200 "seed click"
|
||||||
|
wait element=button childText="In den Korb"
|
||||||
|
click element=button childText="In den Korb"
|
||||||
|
sleep 200 "in korb click"
|
||||||
|
|
||||||
|
|
||||||
|
click element=a href=/Kategorie/Zelte
|
||||||
|
sleep 200 "seed click"
|
||||||
|
wait element=button childText="In den Korb"
|
||||||
|
click element=button childText="In den Korb"
|
||||||
|
sleep 200 "in korb click"
|
||||||
|
|
||||||
|
wait element=button type="button" aria-label="Menge erhöhen"
|
||||||
|
click element=button type="button" aria-label="Menge erhöhen"
|
||||||
|
sleep 200 "menge erhoehen click"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wait element=span class="MuiBadge-badge" childText="3"
|
||||||
|
click element=button child=span(class="MuiBadge-badge" childText="3")
|
||||||
|
sleep 200 "korb click"
|
||||||
|
wait element=button childText="Weiter zur Kasse"
|
||||||
|
click element=button childText="Weiter zur Kasse"
|
||||||
|
sleep 200 "weiter click"
|
||||||
|
wait element=input type="email"
|
||||||
|
fill element=input type="email" value="autotest@growheads.de"
|
||||||
|
sleep 200 "email fill"
|
||||||
|
wait element=input type="password"
|
||||||
|
fill element=input type="password" value="$PASSWORD"
|
||||||
|
sleep 200 "password fill"
|
||||||
|
wait element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
|
click element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
|
sleep 2000 "anmelden click"
|
||||||
|
#dump "isServer"
|
||||||
|
jumpIfNot element=span childText="Server-Warenkorb löschen" jump=2
|
||||||
|
click element=button childText="Weiter"
|
||||||
|
sleep 2000 "anmelden click"
|
||||||
|
|
||||||
|
# Part 2 - Fill in the checkout form
|
||||||
|
|
||||||
|
wait element=input name="firstName"
|
||||||
|
scroll element=span childText="Vorname"
|
||||||
|
fill element=input name="firstName" value="Max"
|
||||||
|
sleep 100 "vorname fill"
|
||||||
|
wait element=input name="lastName"
|
||||||
|
fill element=input name="lastName" value="Muster"
|
||||||
|
sleep 100 "nachname fill"
|
||||||
|
wait element=input name="street"
|
||||||
|
fill element=input name="street" value="Muster"
|
||||||
|
sleep 100 "strasse fill"
|
||||||
|
wait element=input name="houseNumber"
|
||||||
|
fill element=input name="houseNumber" value="420"
|
||||||
|
sleep 100 "hausnummer fill"
|
||||||
|
wait element=input name="postalCode"
|
||||||
|
fill element=input name="postalCode" value="42023"
|
||||||
|
sleep 100 "plz fill"
|
||||||
|
wait element=input name="city"
|
||||||
|
fill element=input name="city" value="Muster"
|
||||||
|
sleep 100 "stadt fill"
|
||||||
|
|
||||||
|
wait element=textarea name="note"
|
||||||
|
scroll element=textarea name="note"
|
||||||
|
fill element=textarea name="note" value="Musteranmerkung"
|
||||||
|
sleep 100 "note fill"
|
||||||
|
wait element=input id="DHL" name="deliveryMethod" value="DHL" type="radio"
|
||||||
|
scroll element=input id="DHL" name="deliveryMethod" value="DHL" type="radio"
|
||||||
|
click element=input id="DHL" name="deliveryMethod" value="DHL" type="radio"
|
||||||
|
sleep 100 "dhl click"
|
||||||
|
wait element=input id="onDelivery" name="paymentMethod" value="onDelivery" type="radio"
|
||||||
|
scroll element=input id="onDelivery" name="paymentMethod" value="onDelivery" type="radio"
|
||||||
|
click element=input id="onDelivery" name="paymentMethod" value="onDelivery" type="radio"
|
||||||
|
sleep 100 "on delivery click"
|
||||||
|
scroll element=button childText="Bestellung abschließen"
|
||||||
|
click element=label childText="Bestimmungen"
|
||||||
|
sleep 100 "checkbox checked"
|
||||||
|
wait element=button childText="Bestellung abschließen"
|
||||||
|
click element=button childText="Bestellung abschließen"
|
||||||
|
sleep 3000 "order completion"
|
||||||
|
|
||||||
77
step1_dpd.test
Normal file
77
step1_dpd.test
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
PlayWrong Test - Complete E-commerce Flow
|
||||||
|
This test demonstrates the full purchase flow from product selection to order completion
|
||||||
|
with beautiful animations and environment variable support
|
||||||
|
Part 1: Load Growheads, put one item in the cart and go to checkout
|
||||||
|
Part 2: Fill in the checkout form
|
||||||
|
Part 3: Login to the email account
|
||||||
|
*/
|
||||||
|
use "Chrome"
|
||||||
|
|
||||||
|
# Part 1 - Load Growheads, put one item in the cart and go to checkout
|
||||||
|
open "https://dev.seedheads.de"
|
||||||
|
sleep 200 "page load"
|
||||||
|
wait element=a href=/Kategorie/Seeds
|
||||||
|
click element=a href=/Kategorie/Seeds
|
||||||
|
sleep 200 "seed click"
|
||||||
|
wait element=button childText="In den Korb"
|
||||||
|
click element=button childText="In den Korb"
|
||||||
|
sleep 200 "in korb click"
|
||||||
|
wait element=span class="MuiBadge-badge" childText="1"
|
||||||
|
click element=button child=span(class="MuiBadge-badge" childText="1")
|
||||||
|
sleep 200 "korb click"
|
||||||
|
wait element=button childText="Weiter zur Kasse"
|
||||||
|
click element=button childText="Weiter zur Kasse"
|
||||||
|
sleep 200 "weiter click"
|
||||||
|
wait element=input type="email"
|
||||||
|
fill element=input type="email" value="autotest@growheads.de"
|
||||||
|
sleep 200 "email fill"
|
||||||
|
wait element=input type="password"
|
||||||
|
fill element=input type="password" value="$PASSWORD"
|
||||||
|
sleep 200 "password fill"
|
||||||
|
wait element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
|
click element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
|
sleep 2000 "anmelden click"
|
||||||
|
#dump "isServer"
|
||||||
|
jumpIfNot element=span childText="Server-Warenkorb löschen" jump=2
|
||||||
|
click element=button childText="Weiter"
|
||||||
|
sleep 2000 "anmelden click"
|
||||||
|
|
||||||
|
# Part 2 - Fill in the checkout form
|
||||||
|
|
||||||
|
wait element=input name="firstName"
|
||||||
|
scroll element=span childText="Vorname"
|
||||||
|
fill element=input name="firstName" value="Max"
|
||||||
|
sleep 100 "vorname fill"
|
||||||
|
wait element=input name="lastName"
|
||||||
|
fill element=input name="lastName" value="Muster"
|
||||||
|
sleep 100 "nachname fill"
|
||||||
|
wait element=input name="street"
|
||||||
|
fill element=input name="street" value="Muster"
|
||||||
|
sleep 100 "strasse fill"
|
||||||
|
wait element=input name="houseNumber"
|
||||||
|
fill element=input name="houseNumber" value="420"
|
||||||
|
sleep 100 "hausnummer fill"
|
||||||
|
wait element=input name="postalCode"
|
||||||
|
fill element=input name="postalCode" value="42023"
|
||||||
|
sleep 100 "plz fill"
|
||||||
|
wait element=input name="city"
|
||||||
|
fill element=input name="city" value="Muster"
|
||||||
|
sleep 100 "stadt fill"
|
||||||
|
|
||||||
|
wait element=textarea name="note"
|
||||||
|
scroll element=textarea name="note"
|
||||||
|
fill element=textarea name="note" value="Musteranmerkung"
|
||||||
|
sleep 100 "note fill"
|
||||||
|
wait element=input id="DPD" name="deliveryMethod" value="DPD" type="radio"
|
||||||
|
scroll element=input id="DPD" name="deliveryMethod" value="DPD" type="radio"
|
||||||
|
click element=input id="DPD" name="deliveryMethod" value="DPD" type="radio"
|
||||||
|
sleep 100 "dpd click"
|
||||||
|
wait element=label childText="Bestimmungen"
|
||||||
|
scroll element=button childText="Bestellung abschließen"
|
||||||
|
click element=label childText="Bestimmungen"
|
||||||
|
sleep 100 "checkbox checked"
|
||||||
|
wait element=button childText="Bestellung abschließen"
|
||||||
|
click element=button childText="Bestellung abschließen"
|
||||||
|
sleep 3000 "order completion"
|
||||||
|
|
||||||
111
step2.test
111
step2.test
@@ -1,65 +1,62 @@
|
|||||||
/*
|
|
||||||
PlayWrong Test - Complete E-commerce Flow
|
|
||||||
This test demonstrates the full purchase flow from product selection to order completion
|
|
||||||
with beautiful animations and environment variable support
|
|
||||||
Part 1: Load Growheads, put one item in the cart and go to checkout
|
|
||||||
Part 2: Fill in the checkout form
|
|
||||||
Part 3: Login to the email account
|
|
||||||
*/
|
|
||||||
use "Chrome"
|
use "Chrome"
|
||||||
|
|
||||||
# Part 1 - Load Growheads, put one item in the cart and go to checkout
|
# Part 3 - Login to the email account
|
||||||
open "https://growheads.de"
|
open "https://mail.growbnb.de/"
|
||||||
sleep 2000 "page load"
|
sleep 100 "page load"
|
||||||
wait element=a href=/Kategorie/Seeds
|
wait element=input name="_user" id="rcmloginuser"
|
||||||
click element=a href=/Kategorie/Seeds
|
fill element=input name="_user" id="rcmloginuser" value="autotest@growheads.de"
|
||||||
sleep 2000 "seed click"
|
sleep 100 "username fill"
|
||||||
wait element=button childText="In den Korb"
|
wait element=input name="_pass" id="rcmloginpwd"
|
||||||
click element=button childText="In den Korb"
|
fill element=input name="_pass" id="rcmloginpwd" value="$PASSWORDMAIL"
|
||||||
sleep 2000 "in korb click"
|
sleep 100 "password fill"
|
||||||
wait element=span class="MuiBadge-badge" childText="1"
|
wait element=button type="submit" id="rcmloginsubmit"
|
||||||
click element=button child=span(class="MuiBadge-badge" childText="1")
|
click element=button type="submit" id="rcmloginsubmit"
|
||||||
sleep 2000 "korb click"
|
sleep 100 "login submit"
|
||||||
wait element=button childText="Weiter zur Kasse"
|
# Wait for and click on the Bestellbestätigung link
|
||||||
click element=button childText="Weiter zur Kasse"
|
wait element=a childText="Bestellbestätigung"
|
||||||
sleep 2000 "weiter click"
|
click element=a childText="Bestellbestätigung"
|
||||||
|
|
||||||
|
|
||||||
|
# Click on "Mehr" button to open dropdown menu
|
||||||
|
wait element=a id="messagemenulink"
|
||||||
|
click element=a id="messagemenulink"
|
||||||
|
sleep 300 "mehr button click"
|
||||||
|
|
||||||
|
# Click on "In neuem Fenster öffnen" link
|
||||||
|
wait element=a id="rcmbtn134"
|
||||||
|
click element=a id="rcmbtn134"
|
||||||
|
|
||||||
|
# Switch to the new window that was opened
|
||||||
|
follow
|
||||||
|
|
||||||
|
# Verify that "Musteranmerkung" exists in the content
|
||||||
|
wait element=p childText="Musteranmerkung"
|
||||||
|
|
||||||
|
# Extract the order URL from the link
|
||||||
|
extract href from element=a childText="https://dev.seedheads.de/profile#W-" to "ORDER_URL"
|
||||||
|
|
||||||
|
wait element=a id="rcmbtn105"
|
||||||
|
click element=a id="rcmbtn105"
|
||||||
|
sleep 300 "email deleted"
|
||||||
|
|
||||||
|
# Now open the extracted URL
|
||||||
|
open "$ORDER_URL"
|
||||||
wait element=input type="email"
|
wait element=input type="email"
|
||||||
fill element=input type="email" value="autotest@growheads.de"
|
fill element=input type="email" value="autotest@growheads.de"
|
||||||
sleep 2000 "email fill"
|
sleep 200 "email fill"
|
||||||
wait element=input type="password"
|
wait element=input type="password"
|
||||||
fill element=input type="password" value="$PASSWORD"
|
fill element=input type="password" value="$PASSWORD"
|
||||||
sleep 2000 "password fill"
|
sleep 200 "password fill"
|
||||||
wait element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
wait element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
click element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
click element=button childText="ANMELDEN" class="MuiButton-fullWidth"
|
||||||
sleep 3000 "anmelden click"
|
sleep 300 "anmelden click"
|
||||||
|
wait element=button childText="Schließen"
|
||||||
# Part 2 - Fill in the checkout form
|
click element=button childText="Schließen"
|
||||||
scroll element=span childText="Vorname"
|
sleep 100 "schließen click"
|
||||||
wait element=input name="firstName"
|
wait element=button class="MuiIconButton-colorError"
|
||||||
fill element=input name="firstName" value="Max"
|
click element=button class="MuiIconButton-colorError"
|
||||||
sleep 1000 "vorname fill"
|
sleep 100 "stornieren click"
|
||||||
wait element=input name="lastName"
|
wait element=button childText="Stornieren"
|
||||||
fill element=input name="lastName" value="Muster"
|
#click element=button childText="Stornieren"
|
||||||
sleep 1000 "nachname fill"
|
sleep 100 "stornieren click"
|
||||||
wait element=input name="street"
|
sleep 10000 "completed"
|
||||||
fill element=input name="street" value="Muster"
|
|
||||||
sleep 1000 "strasse fill"
|
|
||||||
wait element=input name="houseNumber"
|
|
||||||
fill element=input name="houseNumber" value="420"
|
|
||||||
sleep 1000 "hausnummer fill"
|
|
||||||
wait element=input name="postalCode"
|
|
||||||
fill element=input name="postalCode" value="42023"
|
|
||||||
sleep 1000 "plz fill"
|
|
||||||
wait element=input name="city"
|
|
||||||
fill element=input name="city" value="Muster"
|
|
||||||
sleep 1000 "stadt fill"
|
|
||||||
wait element=textarea name="note"
|
|
||||||
fill element=textarea name="note" value="Musteranmerkung"
|
|
||||||
sleep 1000 "note fill"
|
|
||||||
scroll element=button childText="Bestellung abschließen"
|
|
||||||
wait element=label childText="Bestimmungen"
|
|
||||||
click element=label childText="Bestimmungen"
|
|
||||||
sleep 1000 "checkbox checked"
|
|
||||||
wait element=button childText="Bestellung abschließen"
|
|
||||||
sleep 1000 "order completion"
|
|
||||||
dump "order_completed"
|
|
||||||
Reference in New Issue
Block a user