@checksum-ai/runtime 1.1.30 → 1.1.32

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.
@@ -1,9 +1,15 @@
1
- import { ChecksumConfig, IChecksumPage } from "@checksum-ai/runtime";
1
+ import {
2
+ ChecksumConfig,
3
+ IChecksumPage,
4
+ ChecksumConfigEnvironment,
5
+ EnvironmentUser
6
+ } from "@checksum-ai/runtime";
2
7
  import { expect, request } from "@playwright/test";
3
8
 
4
9
  export default async function login(
5
10
  page: IChecksumPage,
6
- config: ChecksumConfig
11
+ config: ChecksumConfig,
12
+ environmentInfo: { environment: ChecksumConfigEnvironment; user: EnvironmentUser }
7
13
  ) {
8
14
  /**
9
15
  * This code provides examples of how to write functions for different login scenarios.
package/checksumlib.js CHANGED
@@ -9653,6 +9653,9 @@
9653
9653
  }
9654
9654
  getElementWindowPlaywright(node2) {
9655
9655
  const elementWindow = node2.ownerDocument.defaultView;
9656
+ if (!elementWindow) {
9657
+ return window.playwright;
9658
+ }
9656
9659
  if (elementWindow !== window && !elementWindow.playwright) {
9657
9660
  elementWindow.playwright = window.playwright;
9658
9661
  }
@@ -9664,13 +9667,6 @@
9664
9667
  node2
9665
9668
  ).generateSelectorAndLocator(node2);
9666
9669
  } catch (error) {
9667
- console.log(
9668
- "get selector and locator error",
9669
- node2.nodeType,
9670
- node2,
9671
- "\n",
9672
- error
9673
- );
9674
9670
  if (retriesLeft > 0) {
9675
9671
  await (0, import_await_sleep.default)(500);
9676
9672
  return this.safeGetSelectorAndLocator(node2, {
@@ -29510,6 +29506,21 @@
29510
29506
  if (events.length === this.currentTraveledNumberOfEvents) {
29511
29507
  return;
29512
29508
  }
29509
+ const lastCheckoutEventIndex = events.reduce(
29510
+ (lastCheckoutEventIndex2, event, index2) => {
29511
+ return event.isCheckout && event.type === EventType.Meta ? index2 : lastCheckoutEventIndex2;
29512
+ },
29513
+ void 0
29514
+ );
29515
+ if (lastCheckoutEventIndex !== void 0) {
29516
+ console.log(
29517
+ "lastCheckoutEventIndex:",
29518
+ lastCheckoutEventIndex,
29519
+ "number of events to render:",
29520
+ events.length - lastCheckoutEventIndex
29521
+ );
29522
+ events.splice(lastCheckoutEventIndex);
29523
+ }
29513
29524
  this.stop();
29514
29525
  this.events = [];
29515
29526
  this.castedEvents = [];
@@ -32631,6 +32642,7 @@
32631
32642
  this.MAX_TESTING_SELECTORS = 50;
32632
32643
  this.MAX_SINGLE_ELEMENT_SELECTORS = 5;
32633
32644
  this.MAX_PROCESSING_TIME = 15e3;
32645
+ this.MAX_MULTICANDIDATE_PROCESSING = 5;
32634
32646
  }
32635
32647
  static {
32636
32648
  __name(this, "PlaywrightCustomLocatorGenerator");
@@ -32699,7 +32711,7 @@
32699
32711
  selector,
32700
32712
  locator: this.getSelectorLocator(selector),
32701
32713
  elements: elements.filter(
32702
- (el) => isInstanceOfHTMLElement(el)
32714
+ (el) => isInstanceOfHTMLElement(el) || el.constructor.name === this.targetElement.constructor.name
32703
32715
  )
32704
32716
  });
32705
32717
  }
@@ -32748,21 +32760,20 @@
32748
32760
  }));
32749
32761
  }
32750
32762
  }
32751
- let filteredCandidated = [];
32763
+ let filteredCandidates = [];
32752
32764
  try {
32753
- filteredCandidated = locatorsCandidates.reduce((acc, candidate) => {
32765
+ for (const candidate of locatorsCandidates) {
32754
32766
  this.checkTimeout();
32755
- acc.push(
32756
- ...candidate.elements.length === 1 ? [candidate] : this.reduceMultiCandidates(candidate)
32767
+ filteredCandidates.push(
32768
+ ...candidate.elements.length === 1 ? [candidate] : await this.reduceMultiCandidates(candidate)
32757
32769
  );
32758
- return acc;
32759
- }, []);
32770
+ }
32760
32771
  } catch (error) {
32761
32772
  if (error instanceof TimeoutError) {
32762
32773
  console.log("Timeout error");
32763
32774
  }
32764
32775
  }
32765
- if (!filteredCandidated?.length) {
32776
+ if (!filteredCandidates?.length) {
32766
32777
  console.log("no single element selector found");
32767
32778
  if (!expandCSSKeyElements) {
32768
32779
  return this.generate(element, cssKeyElements, {
@@ -32777,19 +32788,26 @@
32777
32788
  if (addCSSSelectorGenerator) {
32778
32789
  const cssSelection = await this.addCSSSelectorGenerator();
32779
32790
  if (cssSelection) {
32780
- filteredCandidated.unshift(cssSelection);
32791
+ filteredCandidates.unshift(cssSelection);
32781
32792
  }
32782
32793
  }
32783
32794
  if (this.rootNode === document && useTextContent) {
32784
32795
  const { selector, locator } = await new PlaywrightElementSelectorGenerator().getSelectorAndLocator(
32785
32796
  this.targetElement
32786
32797
  );
32787
- filteredCandidated.unshift({
32798
+ filteredCandidates.unshift({
32788
32799
  selector,
32789
32800
  locator
32790
32801
  });
32791
32802
  }
32792
- return filteredCandidated.map(({ selector, locator }) => ({
32803
+ filteredCandidates = filteredCandidates.concat(
32804
+ await this.addOptionalParentSelector(
32805
+ useTextContent,
32806
+ addCSSSelectorGenerator,
32807
+ expandCSSKeyElements
32808
+ )
32809
+ );
32810
+ return filteredCandidates.map(({ selector, locator }) => ({
32793
32811
  selector,
32794
32812
  locator
32795
32813
  }));
@@ -32869,6 +32887,26 @@
32869
32887
  console.error("Error getting CSS selector", error);
32870
32888
  }
32871
32889
  }
32890
+ /**
32891
+ * If playwright selector for element points at a different element - generate selectors for that element as well
32892
+ * i.e - playwright can point at a parent button if element is a child of it
32893
+ */
32894
+ addOptionalParentSelector(useTextContent, addCSSSelectorGenerator, expandCSSKeyElements) {
32895
+ const playwright = getElementWindowPlaywright(this.targetElement);
32896
+ const playwrightTargetElement = playwright.locator(
32897
+ playwright.selector(this.targetElement)
32898
+ ).element;
32899
+ if (playwrightTargetElement !== this.targetElement && playwrightTargetElement?.contains(this.targetElement)) {
32900
+ return this.generate(playwrightTargetElement, [], {
32901
+ isPartOfListItem: false,
32902
+ isForContextElement: false,
32903
+ useTextContent,
32904
+ addCSSSelectorGenerator,
32905
+ expandCSSKeyElements
32906
+ });
32907
+ }
32908
+ return [];
32909
+ }
32872
32910
  /**
32873
32911
  * Add CSS key features to the element chain, based on attributes that are not covered by playwright selectors
32874
32912
  *
@@ -32885,25 +32923,33 @@
32885
32923
  ({ name, value }) => value.length < 30 && !COVERED_ATTRIBUTES.includes(name)
32886
32924
  );
32887
32925
  newFeatures.forEach((feature) => {
32888
- if (added.length > limit - 1) {
32889
- return;
32890
- }
32891
- const selector = `[${feature.name}${feature.value ? `="${feature.value}"` : ""}]`;
32892
32926
  try {
32893
- if (element.parentElement?.querySelectorAll(selector).length > 1) {
32927
+ if (added.length > limit - 1) {
32928
+ return;
32929
+ }
32930
+ const selector = `[${feature.name}${feature.value ? `="${feature.value}"` : ""}]`;
32931
+ try {
32932
+ if (!element.parentElement || element.parentElement.querySelectorAll(selector).length > 1) {
32933
+ return;
32934
+ }
32935
+ } catch (error) {
32936
+ console.warn(`Error checking selector - ${selector}, continuing`);
32894
32937
  return;
32895
32938
  }
32939
+ if (!cssKeyElements.some((el) => el.selector.includes(selector)) && this.getLocatorBase(element).locator(selector).element === element) {
32940
+ const newFeature = {
32941
+ element,
32942
+ selector
32943
+ };
32944
+ cssKeyElements.push(newFeature);
32945
+ added.push(newFeature);
32946
+ }
32896
32947
  } catch (error) {
32897
- console.warn(`Error checking selector - ${selector}, continuing`);
32898
- return;
32899
- }
32900
- if (!cssKeyElements.some((el) => el.selector.includes(selector)) && this.getLocatorBase(element).locator(selector).element === element) {
32901
- const newFeature = {
32902
- element,
32903
- selector
32904
- };
32905
- cssKeyElements.push(newFeature);
32906
- added.push(newFeature);
32948
+ console.warn(
32949
+ "Error processing css key feature",
32950
+ { name: feature.name, value: feature.value },
32951
+ error
32952
+ );
32907
32953
  }
32908
32954
  });
32909
32955
  element = element.parentElement;
@@ -33061,22 +33107,23 @@
33061
33107
  *
33062
33108
  * @returns array of new candidates that return only one element
33063
33109
  */
33064
- reduceMultiCandidates(candidate) {
33110
+ async reduceMultiCandidates(candidate) {
33111
+ await awaitSleep(100);
33065
33112
  const { elements } = candidate;
33066
33113
  const parts = candidate.selector.split(" >> ");
33067
33114
  const newCandidates = [];
33115
+ const locatorBase = this.getLocatorBase(this.targetElement);
33068
33116
  const addCandidateWithSelector = /* @__PURE__ */ __name((selector) => {
33069
33117
  try {
33070
33118
  newCandidates.push({
33071
33119
  selector,
33072
33120
  locator: this.getSelectorLocator(selector),
33073
- elements: this.getLocatorBase(this.targetElement).locator(selector, candidate.options).elements.filter(
33074
- (el) => isInstanceOfHTMLElement(el)
33075
- ),
33076
33121
  options: candidate.options
33077
33122
  });
33123
+ return newCandidates.length >= this.MAX_MULTICANDIDATE_PROCESSING;
33078
33124
  } catch (error) {
33079
33125
  console.error(error);
33126
+ return false;
33080
33127
  }
33081
33128
  }, "addCandidateWithSelector");
33082
33129
  const addCSSFilterToLocator = /* @__PURE__ */ __name((filter) => candidate.selector + // if we used css selector with tag name - concatentate it with the filter
@@ -33086,6 +33133,12 @@
33086
33133
  this.targetElement.tagName.toLowerCase() + filter
33087
33134
  )}`
33088
33135
  )), "addCSSFilterToLocator");
33136
+ const addIfSingularAndCheckLimit = /* @__PURE__ */ __name((selector) => {
33137
+ if (locatorBase.locator(selector, candidate.options).elements.length === 1) {
33138
+ return addCandidateWithSelector(selector);
33139
+ }
33140
+ return false;
33141
+ }, "addIfSingularAndCheckLimit");
33089
33142
  if (elements.length < 5) {
33090
33143
  const index2 = elements.indexOf(this.targetElement);
33091
33144
  if (index2 !== -1) {
@@ -33098,41 +33151,38 @@
33098
33151
  )) {
33099
33152
  const index2 = Array.from(parent.children).indexOf(this.targetElement);
33100
33153
  if (index2 !== -1) {
33101
- addCandidateWithSelector(
33102
- addCSSFilterToLocator(`:nth-child(${index2 + 1})`)
33103
- );
33154
+ const selector = addCSSFilterToLocator(`:nth-child(${index2 + 1})`);
33155
+ if (addIfSingularAndCheckLimit(selector)) {
33156
+ addCandidateWithSelector(selector);
33157
+ }
33104
33158
  }
33105
33159
  }
33106
- Array.from(this.targetElement.classList).forEach((className) => {
33160
+ for (const className of Array.from(this.targetElement.classList).filter(
33161
+ (cls) => !CLASS_IGNORE_LIST.includes(cls)
33162
+ )) {
33107
33163
  const selector = addCSSFilterToLocator(`.${escapeSelector(className)}`);
33108
33164
  try {
33109
- if (this.getLocatorBase(this.targetElement).locator(
33110
- selector,
33111
- candidate.options
33112
- ).elements.length === 1) {
33113
- addCandidateWithSelector(selector);
33165
+ if (addIfSingularAndCheckLimit(selector)) {
33166
+ return newCandidates;
33114
33167
  }
33115
33168
  } catch (error) {
33116
33169
  console.error(error);
33117
33170
  }
33118
- });
33119
- Array.from(this.targetElement.attributes).filter(
33120
- (attr) => !["class", "style", "id", "href", "src"].includes(attr.name)
33121
- ).forEach((attr) => {
33171
+ }
33172
+ for (const attr of Array.from(this.targetElement.attributes).filter(
33173
+ (attr2) => !COVERED_ATTRIBUTES.includes(attr2.name)
33174
+ )) {
33122
33175
  const selector = addCSSFilterToLocator(
33123
33176
  `[${attr.name}` + (attr.value ? `="${attr.value}"]` : "]")
33124
33177
  );
33125
33178
  try {
33126
- if (this.getLocatorBase(this.targetElement).locator(
33127
- selector,
33128
- candidate.options
33129
- ).elements.length === 1) {
33130
- addCandidateWithSelector(selector);
33179
+ if (addIfSingularAndCheckLimit(selector)) {
33180
+ return newCandidates;
33131
33181
  }
33132
33182
  } catch (error) {
33133
33183
  console.error(error);
33134
33184
  }
33135
- });
33185
+ }
33136
33186
  return newCandidates;
33137
33187
  }
33138
33188
  getAllLocators(element, cssKeyElements, options = {}) {
@@ -33231,7 +33281,7 @@
33231
33281
  }
33232
33282
  return options.returnLocator ? `getByRole('${role}', { ${props.join(", ")} })` : `internal:role=${role}${props.map(([n2, v2]) => `[${n2}=${v2}]`).join("")}`;
33233
33283
  } catch (error) {
33234
- console.error("Error getting role locator", error);
33284
+ console.error("Error getting role locator", error.message);
33235
33285
  }
33236
33286
  }
33237
33287
  getByTextLocator(element, options = {}) {
@@ -33370,7 +33420,14 @@
33370
33420
  __name(isInstanceOfHTMLElement, "isInstanceOfHTMLElement");
33371
33421
 
33372
33422
  // src/lib/test-generator/selectors/compound-selector.ts
33373
- var CLASS_IGNORE_LIST = [":hover", ":focus", ":active"];
33423
+ var CLASS_IGNORE_LIST = [
33424
+ ":hover",
33425
+ ":focus",
33426
+ ":active",
33427
+ "\\:hover",
33428
+ "\\:focus",
33429
+ "\\:active"
33430
+ ];
33374
33431
  var CompoundSelector = class {
33375
33432
  constructor(htmlReducer) {
33376
33433
  this.htmlReducer = htmlReducer;
@@ -33793,7 +33850,7 @@
33793
33850
  return path.join(" ").trim();
33794
33851
  }
33795
33852
  getSelectorPart(element, { useId = false, useClasses = true, useTag = true }) {
33796
- if (useId && element.id) {
33853
+ if (useId && element.id && element.id.match(/^\D/)) {
33797
33854
  return `#${element.id}`;
33798
33855
  }
33799
33856
  let selector = useTag ? element.tagName.toLowerCase() : "";
@@ -34584,7 +34641,8 @@ ${data.locator}`
34584
34641
  this.initialized = true;
34585
34642
  }
34586
34643
  if (initSessionRecorder) {
34587
- this.sessionMirror = new SessionRecorder((event) => {
34644
+ this.sessionMirror = new SessionRecorder((event, isCheckout) => {
34645
+ event.isCheckout = isCheckout;
34588
34646
  window.checksumSendBroadcastMessage?.("rrweb", [event]);
34589
34647
  rrwebEventsStorageManager.onRRwebEvent(event);
34590
34648
  }, config.recordOptions);