@specsage/cli 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser.js +42 -5
- package/lib/cli.rb +10 -1
- package/lib/results_uploader.rb +5 -2
- package/package.json +2 -2
package/lib/browser.js
CHANGED
|
@@ -248,6 +248,7 @@ async function enumerateElements() {
|
|
|
248
248
|
w: Math.round(box.width),
|
|
249
249
|
h: Math.round(box.height)
|
|
250
250
|
},
|
|
251
|
+
locator: el,
|
|
251
252
|
mechanism: 'native',
|
|
252
253
|
source: 'native'
|
|
253
254
|
});
|
|
@@ -336,6 +337,7 @@ async function enumerateElements() {
|
|
|
336
337
|
w: Math.round(box.width),
|
|
337
338
|
h: Math.round(box.height)
|
|
338
339
|
},
|
|
340
|
+
locator: el,
|
|
339
341
|
mechanism: 'scripted',
|
|
340
342
|
source: source
|
|
341
343
|
});
|
|
@@ -427,7 +429,8 @@ async function enumerateElements() {
|
|
|
427
429
|
}
|
|
428
430
|
|
|
429
431
|
lastElements = uniqueElements;
|
|
430
|
-
|
|
432
|
+
// Return elements without the locator property (not JSON-serializable)
|
|
433
|
+
return lastElements.map(({ locator, ...rest }) => rest);
|
|
431
434
|
}
|
|
432
435
|
|
|
433
436
|
async function debugOverlay(x, y) {
|
|
@@ -499,9 +502,26 @@ async function handleCommand(msg) {
|
|
|
499
502
|
if (!element) throw new Error(`Element not found: ${element_id}`);
|
|
500
503
|
lastClickedElement = element; // Store for keypress context
|
|
501
504
|
|
|
502
|
-
|
|
505
|
+
let { x, y, w, h } = element.bounding_box;
|
|
506
|
+
|
|
507
|
+
// Auto-scroll element into view if its center is outside the viewport
|
|
508
|
+
const viewportSize = page.viewportSize();
|
|
509
|
+
let centerY = y + h / 2;
|
|
510
|
+
if (element.locator && (centerY < 0 || centerY >= viewportSize.height)) {
|
|
511
|
+
await element.locator.scrollIntoViewIfNeeded({ timeout: 3000 });
|
|
512
|
+
await new Promise(r => setTimeout(r, 200));
|
|
513
|
+
const newBox = await element.locator.boundingBox();
|
|
514
|
+
if (newBox) {
|
|
515
|
+
x = Math.round(newBox.x);
|
|
516
|
+
y = Math.round(newBox.y);
|
|
517
|
+
w = Math.round(newBox.width);
|
|
518
|
+
h = Math.round(newBox.height);
|
|
519
|
+
element.bounding_box = { x, y, w, h };
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
503
523
|
const centerX = x + w / 2;
|
|
504
|
-
|
|
524
|
+
centerY = y + h / 2;
|
|
505
525
|
|
|
506
526
|
await debugOverlay(centerX, centerY);
|
|
507
527
|
|
|
@@ -559,9 +579,26 @@ async function handleCommand(msg) {
|
|
|
559
579
|
if (!element) throw new Error(`Element not found: ${element_id}`);
|
|
560
580
|
|
|
561
581
|
// Find the select element by its bounding box center
|
|
562
|
-
|
|
582
|
+
let { x, y, w, h } = element.bounding_box;
|
|
583
|
+
|
|
584
|
+
// Auto-scroll element into view if its center is outside the viewport
|
|
585
|
+
const viewportSize = page.viewportSize();
|
|
586
|
+
let centerY = y + h / 2;
|
|
587
|
+
if (element.locator && (centerY < 0 || centerY >= viewportSize.height)) {
|
|
588
|
+
await element.locator.scrollIntoViewIfNeeded({ timeout: 3000 });
|
|
589
|
+
await new Promise(r => setTimeout(r, 200));
|
|
590
|
+
const newBox = await element.locator.boundingBox();
|
|
591
|
+
if (newBox) {
|
|
592
|
+
x = Math.round(newBox.x);
|
|
593
|
+
y = Math.round(newBox.y);
|
|
594
|
+
w = Math.round(newBox.width);
|
|
595
|
+
h = Math.round(newBox.height);
|
|
596
|
+
element.bounding_box = { x, y, w, h };
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
563
600
|
const centerX = x + w / 2;
|
|
564
|
-
|
|
601
|
+
centerY = y + h / 2;
|
|
565
602
|
|
|
566
603
|
// Use Playwright's selectOption by locating the element at the position
|
|
567
604
|
const selectEl = page.locator('select').filter({
|
package/lib/cli.rb
CHANGED
|
@@ -71,6 +71,15 @@ class SpecSageCLI
|
|
|
71
71
|
parser.parse!(@args)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
def build_ci_metadata
|
|
75
|
+
metadata = {}
|
|
76
|
+
metadata[:commit_sha] = ENV['GITHUB_SHA'] if ENV['GITHUB_SHA']
|
|
77
|
+
metadata[:branch] = ENV['GITHUB_REF_NAME'] if ENV['GITHUB_REF_NAME']
|
|
78
|
+
metadata[:pr_number] = ENV['GITHUB_PR_NUMBER'].to_i if ENV['GITHUB_PR_NUMBER']
|
|
79
|
+
metadata[:commit_message] = ENV['GITHUB_COMMIT_MESSAGE'] if ENV['GITHUB_COMMIT_MESSAGE']
|
|
80
|
+
metadata
|
|
81
|
+
end
|
|
82
|
+
|
|
74
83
|
def run_ci_mode
|
|
75
84
|
website = ENV['TARGET_WEBSITE_SLUG']
|
|
76
85
|
api_key = ENV['SPEC_SAGE_API_KEY']
|
|
@@ -108,7 +117,7 @@ class SpecSageCLI
|
|
|
108
117
|
puts ""
|
|
109
118
|
|
|
110
119
|
begin
|
|
111
|
-
run_response = publisher.create_ci_run(website)
|
|
120
|
+
run_response = publisher.create_ci_run(website, ci_metadata: build_ci_metadata)
|
|
112
121
|
server_run_id = run_response['server_run_id']
|
|
113
122
|
rescue ResultsUploader::UploadError => e
|
|
114
123
|
puts "Error creating run: #{e.message}"
|
package/lib/results_uploader.rb
CHANGED
|
@@ -58,8 +58,11 @@ class ResultsUploader
|
|
|
58
58
|
|
|
59
59
|
# Create a CI run on the server
|
|
60
60
|
# Returns hash with server_run_id and base_url
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
# ci_metadata can include: commit_sha, branch, pr_number, commit_message
|
|
62
|
+
def create_ci_run(website_identifier, ci_metadata: {})
|
|
63
|
+
body = { website: website_identifier }
|
|
64
|
+
body.merge!(ci_metadata) if ci_metadata.any?
|
|
65
|
+
post("/api/v1/ci/runs", body)
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
private
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@specsage/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "SpecSage CLI - AI-powered end-to-end testing automation (Node wrapper for Ruby CLI)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,6 +31,6 @@
|
|
|
31
31
|
"node": ">=18.0.0"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"playwright": "
|
|
34
|
+
"playwright": "1.57.0"
|
|
35
35
|
}
|
|
36
36
|
}
|