Refactor test execution and linter to support new 'follow' command for window switching, enhance status tracking with visual indicators, and update extraction syntax for improved clarity
This commit is contained in:
@@ -59,7 +59,7 @@
|
||||
"patterns": [
|
||||
{
|
||||
"name": "keyword.control.playwrong",
|
||||
"match": "\\b(use|open|wait|click|fill|scroll|sleep|dump|break|switch to new window|switch to tab|extract)\\b"
|
||||
"match": "\\b(use|open|wait|click|fill|scroll|sleep|dump|break|follow|switch to tab|extract)\\b"
|
||||
},
|
||||
{
|
||||
"name": "entity.name.function.playwrong",
|
||||
|
||||
425
src/executor.js
425
src/executor.js
@@ -37,8 +37,24 @@ class TestExecutor {
|
||||
try {
|
||||
await this.setup(profileName);
|
||||
|
||||
// Set total commands for status tracking
|
||||
this.totalCommands = commands.length;
|
||||
if (this.statusPage && !this.headless) {
|
||||
await this.updateStatus({ type: 'initializing' }, false);
|
||||
}
|
||||
|
||||
for (const command of commands) {
|
||||
// Update status before executing command
|
||||
if (!this.headless) {
|
||||
await this.updateStatus(command, false);
|
||||
}
|
||||
|
||||
await this.executeCommand(command);
|
||||
|
||||
// Update status after completing command
|
||||
if (!this.headless) {
|
||||
await this.updateStatus(command, true);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -81,6 +97,11 @@ class TestExecutor {
|
||||
|
||||
this.page = await this.context.newPage();
|
||||
|
||||
// Create status window for headed mode AFTER main page so it appears on top
|
||||
if (!this.headless) {
|
||||
await this.createStatusWindow();
|
||||
}
|
||||
|
||||
// Ensure consistent viewport size
|
||||
await this.page.setViewportSize(profile.viewport);
|
||||
|
||||
@@ -206,8 +227,8 @@ class TestExecutor {
|
||||
await this.sleep(command);
|
||||
break;
|
||||
|
||||
case 'switchToNewWindow':
|
||||
await this.switchToNewWindow();
|
||||
case 'follow':
|
||||
await this.followToNewWindow();
|
||||
break;
|
||||
|
||||
case 'switchToTab':
|
||||
@@ -580,82 +601,93 @@ class TestExecutor {
|
||||
|
||||
// Add visual sleep animation with countdown in headed mode
|
||||
if (!this.headless) {
|
||||
await this.page.addStyleTag({
|
||||
content: `
|
||||
.sleep-indicator {
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 20px 40px;
|
||||
border-radius: 10px;
|
||||
font-size: 24px;
|
||||
font-family: Arial, sans-serif;
|
||||
z-index: 999999;
|
||||
pointer-events: none;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.sleep-icon {
|
||||
font-size: 40px;
|
||||
animation: sleepPulse 2s ease-in-out infinite;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@keyframes sleepPulse {
|
||||
0%, 100% {
|
||||
opacity: 0.6;
|
||||
transform: scale(1);
|
||||
try {
|
||||
await this.page.addStyleTag({
|
||||
content: `
|
||||
.sleep-indicator {
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 20px 40px;
|
||||
border-radius: 10px;
|
||||
font-size: 24px;
|
||||
font-family: Arial, sans-serif;
|
||||
z-index: 999999;
|
||||
pointer-events: none;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
|
||||
.sleep-icon {
|
||||
font-size: 40px;
|
||||
animation: sleepPulse 2s ease-in-out infinite;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sleepPulse {
|
||||
0%, 100% {
|
||||
opacity: 0.6;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.countdown {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #4CAF50;
|
||||
}
|
||||
`
|
||||
});
|
||||
|
||||
// Create sleep indicator with countdown
|
||||
await this.page.evaluate(({ duration, message }) => {
|
||||
const indicator = document.createElement('div');
|
||||
indicator.className = 'sleep-indicator';
|
||||
indicator.id = 'sleep-indicator';
|
||||
indicator.innerHTML = `
|
||||
<div class="sleep-icon">💤</div>
|
||||
<div>SLEEP: ${message || 'waiting'}</div>
|
||||
<div class="countdown" id="countdown">${Math.ceil(duration / 1000)}s</div>
|
||||
`;
|
||||
document.body.appendChild(indicator);
|
||||
|
||||
.countdown {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #4CAF50;
|
||||
}
|
||||
`
|
||||
});
|
||||
|
||||
// Create sleep indicator with countdown
|
||||
await this.page.evaluate(({ duration, message }) => {
|
||||
const indicator = document.createElement('div');
|
||||
indicator.className = 'sleep-indicator';
|
||||
indicator.id = 'sleep-indicator';
|
||||
indicator.innerHTML = `
|
||||
<div class="sleep-icon">💤</div>
|
||||
<div>SLEEP: ${message || 'waiting'}</div>
|
||||
<div class="countdown" id="countdown">${Math.ceil(duration / 1000)}s</div>
|
||||
`;
|
||||
document.body.appendChild(indicator);
|
||||
|
||||
// Update countdown every second
|
||||
let remaining = Math.ceil(duration / 1000);
|
||||
const countdownEl = document.getElementById('countdown');
|
||||
|
||||
const interval = setInterval(() => {
|
||||
remaining--;
|
||||
if (countdownEl) {
|
||||
countdownEl.textContent = remaining + 's';
|
||||
}
|
||||
if (remaining <= 0) {
|
||||
clearInterval(interval);
|
||||
const element = document.getElementById('sleep-indicator');
|
||||
if (element) {
|
||||
element.remove();
|
||||
// Update countdown every second
|
||||
let remaining = Math.ceil(duration / 1000);
|
||||
const countdownEl = document.getElementById('countdown');
|
||||
|
||||
const interval = setInterval(() => {
|
||||
remaining--;
|
||||
if (countdownEl) {
|
||||
countdownEl.textContent = remaining + 's';
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
}, { duration: command.milliseconds, message: command.message });
|
||||
if (remaining <= 0) {
|
||||
clearInterval(interval);
|
||||
const element = document.getElementById('sleep-indicator');
|
||||
if (element) {
|
||||
element.remove();
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
}, { duration: command.milliseconds, message: command.message });
|
||||
} catch (error) {
|
||||
// Ignore errors related to destroyed execution context
|
||||
// This happens when the page navigates during sleep
|
||||
if (error.message.includes('Execution context was destroyed') ||
|
||||
error.message.includes('Target page, context or browser has been closed')) {
|
||||
console.log('⚠️ Page navigated during sleep - skipping visual animation');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
@@ -663,7 +695,229 @@ class TestExecutor {
|
||||
});
|
||||
}
|
||||
|
||||
async switchToNewWindow() {
|
||||
async createStatusWindow() {
|
||||
try {
|
||||
// Create a separate browser context for the status window (opens in new window)
|
||||
this.statusContext = await this.browser.newContext({
|
||||
viewport: { width: 700, height: 500 }
|
||||
});
|
||||
|
||||
// Create a page in the new context (this will be in a separate window)
|
||||
this.statusPage = await this.statusContext.newPage();
|
||||
|
||||
// Create the status window HTML
|
||||
const statusHTML = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>PlayWrong Test Status</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid rgba(255,255,255,0.3);
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.logo {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.status-section {
|
||||
background: rgba(255,255,255,0.1);
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.status-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.current-command {
|
||||
font-family: 'Courier New', monospace;
|
||||
background: rgba(0,0,0,0.3);
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
word-break: break-all;
|
||||
min-height: 80px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.stats {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.stat {
|
||||
text-align: center;
|
||||
}
|
||||
.stat-number {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.progress-bar {
|
||||
background: rgba(255,255,255,0.2);
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.progress-fill {
|
||||
background: #4CAF50;
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.status-running { background: #4CAF50; animation: pulse 2s infinite; }
|
||||
.status-waiting { background: #FF9800; }
|
||||
.status-error { background: #F44336; }
|
||||
|
||||
@keyframes pulse {
|
||||
0% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="logo">🎭 PlayWrong</div>
|
||||
<div class="subtitle">Test Execution Status</div>
|
||||
</div>
|
||||
|
||||
<div class="status-section">
|
||||
<div class="status-title">
|
||||
<span id="status-indicator" class="status-indicator status-waiting"></span>
|
||||
Current Command
|
||||
</div>
|
||||
<div id="current-command" class="current-command">Initializing...</div>
|
||||
<div class="progress-bar">
|
||||
<div id="progress-fill" class="progress-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-section">
|
||||
<div class="status-title">Statistics</div>
|
||||
<div class="stats">
|
||||
<div class="stat">
|
||||
<span id="completed-count" class="stat-number">0</span>
|
||||
<span class="stat-label">Completed</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span id="total-count" class="stat-number">0</span>
|
||||
<span class="stat-label">Total</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span id="elapsed-time" class="stat-number">0s</span>
|
||||
<span class="stat-label">Elapsed</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
// Load the status HTML
|
||||
await this.statusPage.setContent(statusHTML);
|
||||
|
||||
// Initialize status tracking
|
||||
this.startTime = Date.now();
|
||||
this.completedCommands = 0;
|
||||
this.totalCommands = 0;
|
||||
|
||||
console.log('🎭 PlayWrong status window created');
|
||||
|
||||
} catch (error) {
|
||||
console.warn('Failed to create status window:', error.message);
|
||||
this.statusPage = null;
|
||||
}
|
||||
}
|
||||
|
||||
async updateStatus(command, isCompleted = false) {
|
||||
if (!this.statusPage || this.headless) return;
|
||||
|
||||
try {
|
||||
// Check if status page is still valid
|
||||
if (this.statusPage.isClosed()) {
|
||||
this.statusPage = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTime = Date.now();
|
||||
const elapsedSeconds = Math.floor((currentTime - this.startTime) / 1000);
|
||||
|
||||
if (isCompleted) {
|
||||
this.completedCommands++;
|
||||
}
|
||||
|
||||
const progress = this.totalCommands > 0 ? (this.completedCommands / this.totalCommands) * 100 : 0;
|
||||
|
||||
await this.statusPage.evaluate(({ command, completed, total, elapsed, progress }) => {
|
||||
// Update current command
|
||||
const commandEl = document.getElementById('current-command');
|
||||
if (commandEl) commandEl.textContent = command;
|
||||
|
||||
// Update statistics
|
||||
const completedEl = document.getElementById('completed-count');
|
||||
if (completedEl) completedEl.textContent = completed;
|
||||
|
||||
const totalEl = document.getElementById('total-count');
|
||||
if (totalEl) totalEl.textContent = total;
|
||||
|
||||
const elapsedEl = document.getElementById('elapsed-time');
|
||||
if (elapsedEl) elapsedEl.textContent = elapsed + 's';
|
||||
|
||||
// Update progress bar
|
||||
const progressEl = document.getElementById('progress-fill');
|
||||
if (progressEl) progressEl.style.width = progress + '%';
|
||||
|
||||
// Update status indicator
|
||||
const indicator = document.getElementById('status-indicator');
|
||||
if (indicator) indicator.className = 'status-indicator status-running';
|
||||
|
||||
}, {
|
||||
command: this.formatCommandForOutput(command),
|
||||
completed: this.completedCommands,
|
||||
total: this.totalCommands,
|
||||
elapsed: elapsedSeconds,
|
||||
progress: progress
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
// Silently ignore status window errors to not interfere with main test
|
||||
// console.log('Status window update failed:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async followToNewWindow() {
|
||||
console.log('🔄 Switching to new tab...');
|
||||
|
||||
// Get all current pages/tabs
|
||||
@@ -755,8 +1009,8 @@ class TestExecutor {
|
||||
return `break "${command.message}"`;
|
||||
case 'sleep':
|
||||
return command.message ? `sleep ${command.milliseconds} "${command.message}"` : `sleep ${command.milliseconds}`;
|
||||
case 'switchToNewWindow':
|
||||
return 'switchToNewWindow';
|
||||
case 'follow':
|
||||
return 'follow';
|
||||
case 'switchToTab':
|
||||
return `switchToTab tabIndex=${command.tabIndex}`;
|
||||
case 'extract':
|
||||
@@ -973,6 +1227,7 @@ class TestExecutor {
|
||||
};
|
||||
}
|
||||
|
||||
// Take screenshot of the main test page only (not status window)
|
||||
await this.page.screenshot(screenshotOptions);
|
||||
|
||||
// Restore viewport after screenshot
|
||||
@@ -992,7 +1247,7 @@ class TestExecutor {
|
||||
const keepElements = ['button', 'input', 'textarea', 'span', 'a'];
|
||||
|
||||
// Attributes we want to keep
|
||||
const keepAttributes = ['id', 'class', 'name', 'value', 'type'];
|
||||
const keepAttributes = ['id', 'name', 'value', 'type'];
|
||||
|
||||
function processElement(element) {
|
||||
const tagName = element.tagName.toLowerCase();
|
||||
@@ -1221,6 +1476,20 @@ class TestExecutor {
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
if (this.statusPage && !this.headless) {
|
||||
try {
|
||||
await this.statusPage.close();
|
||||
} catch (error) {
|
||||
// Ignore errors when closing status window
|
||||
}
|
||||
}
|
||||
if (this.statusContext && !this.headless) {
|
||||
try {
|
||||
await this.statusContext.close();
|
||||
} catch (error) {
|
||||
// Ignore errors when closing status context
|
||||
}
|
||||
}
|
||||
if (this.page) await this.page.close();
|
||||
if (this.context) await this.context.close();
|
||||
if (this.browser) await this.browser.close();
|
||||
|
||||
@@ -6,7 +6,7 @@ class TestLinter {
|
||||
this.info = [];
|
||||
|
||||
// Valid commands
|
||||
this.validCommands = ['use', 'open', 'wait', 'click', 'scroll', 'fill', 'break', 'sleep', 'dump', 'switchToNewWindow', 'switchToTab', 'extract'];
|
||||
this.validCommands = ['use', 'open', 'wait', 'click', 'scroll', 'fill', 'break', 'sleep', 'dump', 'follow', 'switchToTab', 'extract'];
|
||||
|
||||
// Valid HTML elements
|
||||
this.validElements = [
|
||||
@@ -30,7 +30,7 @@ class TestLinter {
|
||||
'break': [],
|
||||
'sleep': ['milliseconds'],
|
||||
'dump': ['name'],
|
||||
'switchToNewWindow': [],
|
||||
'follow': [],
|
||||
'switchToTab': ['tabIndex'],
|
||||
'extract': ['element', 'attribute']
|
||||
};
|
||||
@@ -256,11 +256,7 @@ class TestLinter {
|
||||
const command = line.cleaned.split(' ')[0];
|
||||
|
||||
// Handle multi-word commands
|
||||
if (line.cleaned.startsWith('switch to new window')) {
|
||||
continue; // This is valid
|
||||
}
|
||||
|
||||
if (line.cleaned.startsWith('extract ')) {
|
||||
if (line.cleaned.startsWith('follow')) {
|
||||
continue; // This is valid
|
||||
}
|
||||
|
||||
@@ -268,6 +264,10 @@ class TestLinter {
|
||||
continue; // This is valid
|
||||
}
|
||||
|
||||
if (line.cleaned.startsWith('extract ')) {
|
||||
continue; // This is valid
|
||||
}
|
||||
|
||||
if (!this.validCommands.includes(command)) {
|
||||
this.addError(`Invalid command '${command}'. Valid commands: ${this.validCommands.join(', ')}`, line.lineNumber);
|
||||
}
|
||||
@@ -332,6 +332,12 @@ class TestLinter {
|
||||
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]) {
|
||||
this.addError(`Command '${command}' missing required parameter '${reqParam}'`, line.lineNumber);
|
||||
}
|
||||
|
||||
@@ -186,10 +186,10 @@ class TestParser {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse switch to new window command
|
||||
if (line.startsWith('switch to new window')) {
|
||||
// Parse follow command (previously switch to new window)
|
||||
if (line.startsWith('follow')) {
|
||||
return {
|
||||
type: 'switchToNewWindow'
|
||||
type: 'follow'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -204,9 +204,9 @@ class TestParser {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse extract command: extract href from element=a target="_blank" to variable="ORDER_URL"
|
||||
// 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+variable="([^"]+)"/);
|
||||
const match = line.match(/extract\s+(\w+)\s+from\s+(.+?)\s+to\s+"([^"]+)"/);
|
||||
if (match) {
|
||||
const attribute = match[1];
|
||||
const selectorPart = match[2];
|
||||
|
||||
43
step1.test
43
step1.test
@@ -9,7 +9,7 @@ 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://growheads.de"
|
||||
open "https://dev.seedheads.de"
|
||||
sleep 2000 "page load"
|
||||
wait element=a href=/Kategorie/Seeds
|
||||
click element=a href=/Kategorie/Seeds
|
||||
@@ -63,7 +63,7 @@ sleep 1000 "checkbox checked"
|
||||
wait element=button childText="Bestellung abschließen"
|
||||
click element=button childText="Bestellung abschließen"
|
||||
sleep 3000 "order completion"
|
||||
*/
|
||||
|
||||
# Part 3 - Login to the email account
|
||||
open "https://mail.growbnb.de/"
|
||||
sleep 100 "page load"
|
||||
@@ -89,33 +89,38 @@ sleep 300 "mehr button click"
|
||||
# Click on "In neuem Fenster öffnen" link
|
||||
wait element=a id="rcmbtn134"
|
||||
click element=a id="rcmbtn134"
|
||||
sleep 3000 "new window open"
|
||||
|
||||
# Switch to the new window that was opened
|
||||
switch to new window
|
||||
|
||||
# Wait a bit more for the new window to fully load
|
||||
sleep 2000 "new window load"
|
||||
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://growheads.de/profile#W-" to variable="ORDER_URL"
|
||||
extract href from element=a childText="https://dev.seedheads.de/profile#W-" to "ORDER_URL"
|
||||
|
||||
sleep 300 "bestellbestätigung click"
|
||||
dump "after_bestellbestaetigung_click"
|
||||
sleep 3000 "bestellbestätigung click"
|
||||
# Delete the email by clicking "Löschen"
|
||||
wait element=a id="rcmbtn105"
|
||||
click element=a id="rcmbtn105"
|
||||
sleep 1000 "email deleted"
|
||||
sleep 300 "email deleted"
|
||||
|
||||
# Now open the extracted URL
|
||||
open "$ORDER_URL"
|
||||
sleep 2000 "order page load"
|
||||
dump "order_page_from_extracted_url"
|
||||
|
||||
dump "after_new_window_click"
|
||||
|
||||
sleep 30000 "login submit"
|
||||
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 300 "anmelden click"
|
||||
wait element=button childText="Schließen"
|
||||
click element=button childText="Schließen"
|
||||
sleep 100 "schließen click"
|
||||
wait element=button class="MuiIconButton-colorError"
|
||||
click element=button class="MuiIconButton-colorError"
|
||||
sleep 100 "stornieren click"
|
||||
wait element=button childText="Stornieren"
|
||||
click element=button childText="Stornieren"
|
||||
sleep 100 "stornieren click"
|
||||
sleep 10000 "completed"
|
||||
Reference in New Issue
Block a user