@skyramp/skyramp 1.3.16 → 1.3.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyramp/skyramp",
3
- "version": "1.3.16",
3
+ "version": "1.3.18",
4
4
  "description": "module for leveraging skyramp cli functionality",
5
5
  "scripts": {
6
6
  "lint": "eslint 'src/**/*.js' 'src/**/*.ts' --fix",
@@ -320,6 +320,7 @@ class GoJSDiagram {
320
320
  anchorOffsetY = 0,
321
321
  } = opts;
322
322
 
323
+ // Phase 1: Add the node via GoJS model API (no selection here).
323
324
  await this._evaluateInFrame(([diagSel, palSel, nodeKey, nodeCat, anKey, offX, offY]) => {
324
325
  const go = window.go;
325
326
  if (!go) throw new Error('GoJSDiagram: GoJS not found on window');
@@ -361,6 +362,32 @@ class GoJSDiagram {
361
362
  diagram.commitTransaction(txName);
362
363
  }
363
364
  }, [diagramSelector, paletteSelector ?? '', key, category, anchorKey ?? '', anchorOffsetX, anchorOffsetY]);
365
+
366
+ // Phase 2: Wait for GoJS to finish its layout pass after the transaction.
367
+ await this._page.waitForTimeout(300);
368
+
369
+ // Phase 3: Select via GoJS API (fires ChangedSelection for apps that listen to it).
370
+ // Returns debug info so callers can log selection state without cross-origin console issues.
371
+ await this._evaluateInFrame(([diagSel, nodeKey]) => {
372
+ const go = window.go;
373
+ if (!go) return { error: 'no go' };
374
+ const diagramDiv = document.querySelector(diagSel);
375
+ if (!diagramDiv) return { error: 'no div' };
376
+ const diagram = go.Diagram.fromDiv(diagramDiv);
377
+ if (!diagram) return { error: 'no diagram' };
378
+ const node = diagram.findNodeForKey(nodeKey);
379
+ if (!node) return { error: 'node not found', key: nodeKey };
380
+ diagram.select(node);
381
+ return { selected: node.isSelected, selectionCount: diagram.selection.count };
382
+ }, [diagramSelector, key]);
383
+
384
+ // Phase 4: Click at the node's page-level coordinates using Playwright's mouse.
385
+ // This fires the full browser event chain (including any overlay div handlers)
386
+ // for apps that rely on click events rather than GoJS's ChangedSelection.
387
+ // _resolveDiagramNode() computes page-level coords by combining the canvas
388
+ // bounding box (via Playwright) with GoJS transformDocToView() (via frame.evaluate).
389
+ const coords = await this._resolveDiagramNode(diagramSelector, key);
390
+ await this._page.mouse.click(coords.x, coords.y);
364
391
  }
365
392
  }
366
393
 
@@ -146,6 +146,9 @@ interface GenerateRestTestOptions {
146
146
  consumerMode?: boolean;
147
147
  providerOutput?: string;
148
148
  consumerOutput?: string;
149
+ skipProvisionParents?: boolean;
150
+ mockPort?: number;
151
+ optionalFields?: boolean;
149
152
  }
150
153
 
151
154
  interface GenerateRestMockOptions {
@@ -172,6 +175,8 @@ interface GenerateRestMockOptions {
172
175
  apiSchema?: string[];
173
176
  traceFilePath?: string;
174
177
  entrypoint?: string;
178
+ mockPort?: number;
179
+ optionalFields?: boolean;
175
180
  }
176
181
 
177
182
  interface SendScenarioOptions {
@@ -111,7 +111,8 @@ const generateRestTestWrapper = lib.func('generateRestTestWrapper', 'string', [
111
111
  'string', // providerOutput (contract test)
112
112
  'string', // consumerOutput (contract test)
113
113
  'bool', // skipProvisionParents
114
- 'int' // mockPort
114
+ 'int', // mockPort
115
+ 'bool' // optionalFields
115
116
  ]);
116
117
  const generateRestMockWrapper = lib.func('generateRestMockWrapper', 'string', [
117
118
  'string', // uri
@@ -136,7 +137,8 @@ const generateRestMockWrapper = lib.func('generateRestMockWrapper', 'string', [
136
137
  'string', // apiSchema
137
138
  'string', // traceFilePath
138
139
  'string', // entryPoint
139
- 'int' // mockPort
140
+ 'int', // mockPort
141
+ 'bool' // optionalFields
140
142
  ]);
141
143
  const traceCollectWrapper = lib.func('traceCollectWrapper', 'string', ['string', 'string', 'bool', 'string', 'string']);
142
144
  const analyzeOpenapiWrapper = lib.func('analyzeOpenapiWrapper', 'string', ['string', 'string']);
@@ -960,6 +962,7 @@ class SkyrampClient {
960
962
  options.consumerOutput || "",
961
963
  options.skipProvisionParents || false,
962
964
  options.mockPort || 0,
965
+ options.optionalFields || false,
963
966
  (err, res) => {
964
967
  if (err) {
965
968
  reject(err);
@@ -997,6 +1000,7 @@ class SkyrampClient {
997
1000
  options.traceFilePath || "",
998
1001
  options.entrypoint || "",
999
1002
  options.mockPort || 0,
1003
+ options.optionalFields || false,
1000
1004
  (err, res) => {
1001
1005
  if (err) {
1002
1006
  reject(err);
@@ -189,6 +189,11 @@ async function retryWithLLM(skyrampLocator, error) {
189
189
  throw error
190
190
  }
191
191
 
192
+ const consecutiveCount = skyrampLocator._skyrampPage.incrementConsecutiveLLMCount();
193
+ if (consecutiveCount >= 3) {
194
+ throw new Error(`LLM-based locator retry limit exceeded (${consecutiveCount} consecutive attempts). Please add "data-testid" attributes for more stable locators.`);
195
+ }
196
+
192
197
  let locatorStr = skyrampLocator._locator.toString();
193
198
 
194
199
  if (!shouldAttemptImprovement(errorMessage, errorType)) {
@@ -241,7 +246,8 @@ async function retryWithLLM(skyrampLocator, error) {
241
246
 
242
247
  if (locatorCount == 1) {
243
248
  const func = newLocator[skyrampLocator.execFname];
244
- return func.call(newLocator, skyrampLocator.execParam, skyrampLocator.execArgs).then(result => {
249
+ try {
250
+ const result = await func.call(newLocator, skyrampLocator.execParam, skyrampLocator.execArgs);
245
251
  console.log(`✅ SUCCESS! Used selector: ${newLocator} instead of ${skyrampLocator._locator}`);
246
252
  console.log(` ${parseErrorStack(error.stack)}`);
247
253
 
@@ -249,10 +255,10 @@ async function retryWithLLM(skyrampLocator, error) {
249
255
  skyrampLocator.locatorCount = 1;
250
256
  skyrampLocator._skyrampPage.addLLMChoices(skyrampLocator._locator, newLocator, error.stack);
251
257
  return result;
252
- }).catch(error => {
258
+ } catch (innerError) {
253
259
  // if it fails, move to the next one
254
- debug(`retrying with LLM failed at ${skyrampLocator._locator} replaced by {newLocator}`, error.name);
255
- });
260
+ debug(`retrying with LLM failed at ${skyrampLocator._locator} replaced by ${newLocator}`, innerError.name);
261
+ }
256
262
  }
257
263
  } catch {
258
264
  continue
@@ -400,6 +406,7 @@ class SkyrampPlaywrightLocator {
400
406
  // if it fails, there could be potentially a hydration issue we can retry after a little wait time
401
407
  try {
402
408
  return await this.execute().then(result => {
409
+ this._skyrampPage.resetConsecutiveLLMCount();
403
410
  return this._skyrampPage.checkNavigation(currentUrl, result);
404
411
  });
405
412
  } catch (error) {
@@ -412,6 +419,7 @@ class SkyrampPlaywrightLocator {
412
419
 
413
420
  // Is this really necessary?
414
421
  await this.execute(true).then(result => {
422
+ this._skyrampPage.resetConsecutiveLLMCount();
415
423
  return this._skyrampPage.checkNavigation(currentUrl, result);
416
424
  }).catch(() => {
417
425
  debug(` failed second time and execute previous locator ${this._previousLocator._locator} again`);
@@ -458,6 +466,7 @@ class SkyrampPlaywrightLocator {
458
466
 
459
467
  // this will likely fail, but we try to get error message generated by playwright
460
468
  return this.execute().then(result => {
469
+ this._skyrampPage.resetConsecutiveLLMCount();
461
470
  return this._skyrampPage.checkNavigation(currentUrl, result);
462
471
  }).catch(error => {
463
472
  return this._retryWithLLM(error, this.newMultiLocatorErrorMsg());
@@ -482,6 +491,7 @@ class SkyrampPlaywrightLocator {
482
491
  try {
483
492
  // then execute the current one
484
493
  return await this.execute().then(result => {
494
+ this._skyrampPage.resetConsecutiveLLMCount();
485
495
  return this._skyrampPage.checkNavigation(currentUrl, result);
486
496
  });
487
497
  } catch (error) {
@@ -490,6 +500,7 @@ class SkyrampPlaywrightLocator {
490
500
  // wait for some time and re execute
491
501
  await this.wait(defaultWaitForTimeout);
492
502
  return this.execute(true).then(result => {
503
+ this._skyrampPage.resetConsecutiveLLMCount();
493
504
  return this._skyrampPage.checkNavigation(currentUrl, result);
494
505
  }).catch(newError => {
495
506
  return this._retryWithLLM(newError, this.newPrevHydrationErrorMsg());
@@ -517,6 +528,7 @@ class SkyrampPlaywrightLocator {
517
528
 
518
529
  try {
519
530
  return await this.execute().then(result => {
531
+ this._skyrampPage.resetConsecutiveLLMCount();
520
532
  return this._skyrampPage.checkNavigation(currentUrl, result);
521
533
  });
522
534
  } catch (error) {
@@ -524,6 +536,7 @@ class SkyrampPlaywrightLocator {
524
536
  debug(`${this._locator} failed at first try. attempting again with some timeout`);
525
537
  await this.wait(defaultWaitForTimeout);
526
538
  return this.execute(true).then(result=> {
539
+ this._skyrampPage.resetConsecutiveLLMCount();
527
540
  return this._skyrampPage.checkNavigation(currentUrl, result);
528
541
  }).catch(newError => {
529
542
  if (newError.name == "TimeoutError") {
@@ -1265,6 +1278,18 @@ class SkyrampPlaywrightPage {
1265
1278
  return result;
1266
1279
  }
1267
1280
 
1281
+ incrementConsecutiveLLMCount() {
1282
+ if (this.consecutiveLLMCount == undefined) {
1283
+ this.consecutiveLLMCount = 0;
1284
+ }
1285
+ this.consecutiveLLMCount++;
1286
+ return this.consecutiveLLMCount;
1287
+ }
1288
+
1289
+ resetConsecutiveLLMCount() {
1290
+ this.consecutiveLLMCount = 0;
1291
+ }
1292
+
1268
1293
  addLLMChoices(originalLocator, newLocator, stack) {
1269
1294
  if (this.llmChoices == undefined) {
1270
1295
  this.llmChoices = []