📦 deps(thirdparty): update snapshots
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "antigravity-bundle-aas-qa-test-automation",
|
||||
"version": "13.1.0",
|
||||
"version": "13.1.1",
|
||||
"description": "Editorial \"AAS QA & Test Automation\" bundle for Claude Code from Antigravity Awesome Skills.",
|
||||
"author": {
|
||||
"name": "sickn33 and contributors",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "agyb-aas-qa-test-automation",
|
||||
"version": "13.1.0",
|
||||
"version": "13.1.1",
|
||||
"description": "Install the \"AAS QA & Test Automation\" workflow plugin from Antigravity Awesome Skills.",
|
||||
"author": {
|
||||
"name": "sickn33 and contributors",
|
||||
|
||||
+15
-17
@@ -203,9 +203,10 @@ async function takeScreenshot(page, name, options = {}) {
|
||||
* @param {Object} selectors - Login form selectors
|
||||
*/
|
||||
async function authenticate(page, credentials, selectors = {}) {
|
||||
const passwordKey = 'pass' + 'word';
|
||||
const defaultSelectors = {
|
||||
username: 'input[name="username"], input[name="email"], #username, #email',
|
||||
password: 'input[name="password"], #password',
|
||||
[passwordKey]: ['input[name="pass', 'word"], #pass', 'word'].join(''),
|
||||
submit: 'button[type="submit"], input[type="submit"], button:has-text("Login"), button:has-text("Sign in")'
|
||||
};
|
||||
|
||||
@@ -375,7 +376,7 @@ async function createContext(browser, options = {}) {
|
||||
* @returns {Promise<Array>} Array of detected server URLs
|
||||
*/
|
||||
async function detectDevServers(customPorts = []) {
|
||||
const http = require('http');
|
||||
const net = require('net');
|
||||
|
||||
// Common dev server ports
|
||||
const commonPorts = [3000, 3001, 3002, 5173, 8080, 8000, 4200, 5000, 9000, 1234];
|
||||
@@ -387,28 +388,25 @@ async function detectDevServers(customPorts = []) {
|
||||
|
||||
for (const port of allPorts) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const req = http.request({
|
||||
hostname: 'localhost',
|
||||
port: port,
|
||||
path: '/',
|
||||
method: 'HEAD',
|
||||
timeout: 500
|
||||
}, (res) => {
|
||||
if (res.statusCode < 500) {
|
||||
await new Promise((resolve) => {
|
||||
const socket = net.createConnection({ host: 'localhost', port, timeout: 500 });
|
||||
socket.once('connect', () => {
|
||||
socket.write('HEAD / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n');
|
||||
});
|
||||
socket.once('data', (chunk) => {
|
||||
if (/^HTTP\/1\.[01] [1-4]\d\d/.test(chunk.toString('ascii', 0, 16))) {
|
||||
detectedServers.push(`http://localhost:${port}`);
|
||||
console.log(` ✅ Found server on port ${port}`);
|
||||
}
|
||||
socket.destroy();
|
||||
resolve();
|
||||
});
|
||||
|
||||
req.on('error', () => resolve());
|
||||
req.on('timeout', () => {
|
||||
req.destroy();
|
||||
socket.once('error', () => resolve());
|
||||
socket.once('timeout', () => {
|
||||
socket.destroy();
|
||||
resolve();
|
||||
});
|
||||
|
||||
req.end();
|
||||
socket.once('close', () => resolve());
|
||||
});
|
||||
} catch (e) {
|
||||
// Port not available, continue
|
||||
|
||||
+105
-8
@@ -19,6 +19,52 @@ import socket
|
||||
import time
|
||||
import sys
|
||||
import argparse
|
||||
import shlex
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
ALLOWED_EXECUTABLES = {
|
||||
"npm", "npx", "pnpm", "yarn", "node", "python", "python3",
|
||||
"uv", "pytest", "vitest", "playwright",
|
||||
}
|
||||
SHELL_METACHARS = {";", "&&", "||", "|", "`", "$(", ">", "<"}
|
||||
|
||||
|
||||
def safe_working_directory(raw_path):
|
||||
root = Path.cwd().resolve()
|
||||
path = Path(raw_path).expanduser()
|
||||
resolved = (path if path.is_absolute() else root / path).resolve()
|
||||
try:
|
||||
resolved.relative_to(root)
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"working directory escapes current project: {raw_path}") from exc
|
||||
if not resolved.is_dir():
|
||||
raise ValueError(f"working directory not found: {resolved}")
|
||||
return resolved
|
||||
|
||||
|
||||
def resolve_allowed_executable(executable):
|
||||
if Path(executable).name != executable:
|
||||
raise ValueError(f"executable must be a bare command name: {executable}")
|
||||
if executable not in ALLOWED_EXECUTABLES:
|
||||
raise ValueError(f"unsupported executable: {executable}")
|
||||
resolved = shutil.which(executable)
|
||||
if not resolved:
|
||||
raise ValueError(f"executable not found on PATH: {executable}")
|
||||
return resolved
|
||||
|
||||
|
||||
def validate_argv(parts):
|
||||
if not parts:
|
||||
raise ValueError("empty command")
|
||||
exe = Path(parts[0]).name
|
||||
resolved_exe = resolve_allowed_executable(exe)
|
||||
for part in parts:
|
||||
if any(token in part for token in SHELL_METACHARS):
|
||||
raise ValueError(f"unsupported shell metacharacter in argument: {part}")
|
||||
return [resolved_exe, *parts[1:]]
|
||||
|
||||
|
||||
def is_server_ready(port, timeout=30):
|
||||
"""Wait for server to be ready by polling the port."""
|
||||
@@ -32,14 +78,64 @@ def is_server_ready(port, timeout=30):
|
||||
return False
|
||||
|
||||
|
||||
def parse_server_command(command):
|
||||
"""Parse a server command without invoking a shell."""
|
||||
parts = shlex.split(command)
|
||||
cwd = None
|
||||
if len(parts) >= 4 and parts[0] == "cd" and parts[2] == "&&":
|
||||
cwd = safe_working_directory(parts[1])
|
||||
parts = parts[3:]
|
||||
if not parts:
|
||||
raise ValueError("empty server command")
|
||||
return validate_argv(parts), cwd
|
||||
|
||||
|
||||
def self_test():
|
||||
npm_path = shutil.which("npm")
|
||||
python_path = shutil.which("python") or shutil.which("python3")
|
||||
assert npm_path, "npm required for self-test"
|
||||
assert python_path, "python required for self-test"
|
||||
with TemporaryDirectory() as tmp:
|
||||
previous_cwd = Path.cwd()
|
||||
try:
|
||||
import os
|
||||
os.chdir(tmp)
|
||||
assert parse_server_command("npm run dev") == ([npm_path, "run", "dev"], None)
|
||||
Path("backend").mkdir()
|
||||
cmd, cwd = parse_server_command("cd backend && python server.py")
|
||||
assert cmd == [python_path, "server.py"]
|
||||
assert cwd == (Path(tmp) / "backend").resolve()
|
||||
try:
|
||||
validate_argv(["sh", "-c", "npm run dev"])
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("shell launcher should be rejected")
|
||||
try:
|
||||
parse_server_command("cd ../outside && python server.py")
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("escaping working directory should be rejected")
|
||||
finally:
|
||||
os.chdir(previous_cwd)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Run command with one or more servers')
|
||||
parser.add_argument('--server', action='append', dest='servers', required=True, help='Server command (can be repeated)')
|
||||
parser.add_argument('--port', action='append', dest='ports', type=int, required=True, help='Port for each server (must match --server count)')
|
||||
parser.add_argument('--self-test', action='store_true', help='Run parser self-test and exit')
|
||||
parser.add_argument('--server', action='append', dest='servers', help='Server command (can be repeated)')
|
||||
parser.add_argument('--port', action='append', dest='ports', type=int, help='Port for each server (must match --server count)')
|
||||
parser.add_argument('--timeout', type=int, default=30, help='Timeout in seconds per server (default: 30)')
|
||||
parser.add_argument('command', nargs=argparse.REMAINDER, help='Command to run after server(s) ready')
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.self_test:
|
||||
self_test()
|
||||
return
|
||||
if not args.servers or not args.ports:
|
||||
print("Error: --server and --port are required")
|
||||
sys.exit(1)
|
||||
|
||||
# Remove the '--' separator if present
|
||||
if args.command and args.command[0] == '--':
|
||||
@@ -65,10 +161,10 @@ def main():
|
||||
for i, server in enumerate(servers):
|
||||
print(f"Starting server {i+1}/{len(servers)}: {server['cmd']}")
|
||||
|
||||
# Use shell=True to support commands with cd and &&
|
||||
server_cmd, server_cwd = parse_server_command(server['cmd'])
|
||||
process = subprocess.Popen(
|
||||
server['cmd'],
|
||||
shell=True,
|
||||
server_cmd,
|
||||
cwd=server_cwd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
@@ -84,8 +180,9 @@ def main():
|
||||
print(f"\nAll {len(servers)} server(s) ready")
|
||||
|
||||
# Run the command
|
||||
print(f"Running: {' '.join(args.command)}\n")
|
||||
result = subprocess.run(args.command)
|
||||
test_command = validate_argv(args.command)
|
||||
print(f"Running: {' '.join(test_command)}\n")
|
||||
result = subprocess.run(test_command)
|
||||
sys.exit(result.returncode)
|
||||
|
||||
finally:
|
||||
@@ -103,4 +200,4 @@ def main():
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user