@checksum-ai/runtime 1.1.22 → 1.1.23
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/checksum-root/README.md +2 -64
- package/checksumlib.js +209 -33
- package/cli.js +554 -53
- package/index.js +70 -67
- package/package.json +1 -1
package/checksum-root/README.md
CHANGED
|
@@ -1,65 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
### Checksum.ai Runtime
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
1. Run `npm install -D checksumai`.
|
|
6
|
-
2. Navigate to the directory where you want to add Checksum tests and run `npx checksumai init`.
|
|
7
|
-
3. Run `npx playwright install --with-deps` to install Playwright dependencies.
|
|
8
|
-
4. Edit `checksum.config.ts` to include necessary configurations such as:
|
|
9
|
-
- `apiKey`
|
|
10
|
-
- `baseURL`
|
|
11
|
-
- `username`
|
|
12
|
-
- `password`
|
|
13
|
-
5. Update `login.ts` with your login function using Playwright. See the Login Function section below for guidance.
|
|
14
|
-
6. Run `npx checksumai test` to execute the example test and verify successful login.
|
|
15
|
-
7. If you haven't already, visit [app.checksum.ai](https://app.checksum.ai) to complete the configuration and generate a test. Then, wait for the pull request (PR) to be created and approve it.
|
|
16
|
-
|
|
17
|
-
## Login Function
|
|
18
|
-
|
|
19
|
-
1. This function is executed at the start of each test.
|
|
20
|
-
2. We recommend using a consistent seeded user for every test. For example, before each test, call a webhook that creates a user, seeds it with data, and returns the username and password. This approach ensures test reliability and allows parallel test execution. Configure this webhook [in your project](https://app.checksum.ai/#/settings/wizard) for consistent test generation.
|
|
21
|
-
3. After logging in, assert that the login was successful. Playwright waits for assertions to be correct, ensuring that the page is ready for interaction before proceeding.
|
|
22
|
-
4. To reuse authentication states between tests, refer to the Playwright guide on [authentication](https://playwright.dev/docs/auth). At the start of the login function, check if the user is already authenticated and return if so.
|
|
23
|
-
|
|
24
|
-
## Checksum AI Magic
|
|
25
|
-
|
|
26
|
-
The tests generated by Checksum are based on Playwright. When executed using the Checksum CLI with an API key, Checksum enhances Playwright's functionality, improving test reliability and automatically maintaining tests.
|
|
27
|
-
|
|
28
|
-
### Autonomous Test Agent
|
|
29
|
-
|
|
30
|
-
Checksum runs your Playwright tests regularly, but we added a few extra features to make tests more reliable. All of the features can be turned on/off through `checksum.config.ts`
|
|
31
|
-
|
|
32
|
-
**Smart Selectors**
|
|
33
|
-
When generating tests, Checksum stores extensive metadata for each action (see the `test-data` folder). If a traditional selector fails, this metadata is used for correction. For example, if a test identifies an element by its ID but the ID changes, Checksum utilizes other data points (e.g., element class, text, parents) to locate the element. Use the `checksumSelector("<id>")` method to link an action to its metadata. Do not alter the IDs.
|
|
34
|
-
|
|
35
|
-
**Checksum AI**
|
|
36
|
-
If Smart Selectors also fail, Checksum's custom-trained model can regenerate the failed section of the test. In such cases, the model might add, remove, or alter actions to achieve the same objectives, without changing the assertions. The assumption is that as long as the assertions pass, the model has successfully fixed the test. Use the `.checksumAI("<natural language description of the test>")` method to guide the model in fixing the test.
|
|
37
|
-
|
|
38
|
-
You can modify the description as needed for our model. Additionally, you can include steps with only ChecksumAI descriptions, prompting our model to generate the Playwright code. For example, `await page.checksumAI("Click on 'New Task' button")` without the actual action will have our model generate the necessary Playwright code. You can even author entire tests in this manner.
|
|
39
|
-
|
|
40
|
-
### Run Modes
|
|
41
|
-
|
|
42
|
-
Checksum offers three run modes:
|
|
43
|
-
|
|
44
|
-
1. **Normal** - Tests are executed using the Autonomous Test Agent as defined in the config file.
|
|
45
|
-
2. **Heal** - If the Autonomous Test Agent corrects a test, a new test file with the fix is created. By default, this file is created locally, but you can also configure the Agent to open a PR to your GitHub repository by setting `autoHealPRs` to true.
|
|
46
|
-
3. **Refactor (Work in Progress)** - The Autonomous Test Agent runs the test and, for each action, regenerates a regular Playwright selector, a Smart Selector, and a Checksum AI description.
|
|
47
|
-
|
|
48
|
-
### Mock Data
|
|
49
|
-
|
|
50
|
-
When generating tests, Checksum records all backend responses, allowing tests to run in the same backend context. This is particularly useful for debugging or initial test runs, especially if your testing database/context differs from that used for test generation. Note that if your backend response format changes, the mocked data may not work as expected.
|
|
51
|
-
|
|
52
|
-
### CLI Commands
|
|
53
|
-
|
|
54
|
-
1. `init` - Initialize the Checksum directory and configurations.
|
|
55
|
-
2. `test` - Run Checksum tests. Accepts all [Playwright command line flags](https://playwright.dev/docs/test-cli). To override `checksum.config.ts`, pass full or partial JSON as a string, e.g., `--checksum-config='{"baseURL": "https://example.com"}'`.
|
|
56
|
-
|
|
57
|
-
## Running with GitHub Actions
|
|
58
|
-
|
|
59
|
-
See the example file `github-actions.example.yml`.
|
|
60
|
-
|
|
61
|
-
## Troubleshooting
|
|
62
|
-
|
|
63
|
-
**Q: I'm seeing various exceptions when I run "npx checksumai test", even before the test starts.**
|
|
64
|
-
|
|
65
|
-
A: If you had a pre-installed version of Playwright, it might not be compatible with Checksum. Uninstall the Playwright and Checksum libraries, delete the relevant folder from `node_modules`, and run `npm install -D checksumai`.
|
|
3
|
+
To learn more about how to run tests using the Checksum.ai Runtime please refer to the [Github repository's readme](https://github.com/checksum-ai/checksum-ai-runtime?tab=readme-ov-file#checksumai-runtime)
|
package/checksumlib.js
CHANGED
|
@@ -22123,10 +22123,16 @@
|
|
|
22123
22123
|
this.start = /* @__PURE__ */ __name(() => {
|
|
22124
22124
|
this.stopRRWebRecording = record({
|
|
22125
22125
|
emit: this.eventHandler,
|
|
22126
|
+
// [rrweb config changes]
|
|
22126
22127
|
sampling: {
|
|
22127
22128
|
mousemove: false
|
|
22128
22129
|
},
|
|
22129
|
-
|
|
22130
|
+
//mousemoveWait: 100,
|
|
22131
|
+
userTriggeredOnInput: true,
|
|
22132
|
+
maskInputOptions: {
|
|
22133
|
+
password: false
|
|
22134
|
+
// Do not mask password inputs
|
|
22135
|
+
}
|
|
22130
22136
|
});
|
|
22131
22137
|
if (this.logNativeMutationObserver) {
|
|
22132
22138
|
this.selfObserve();
|
|
@@ -22733,32 +22739,40 @@
|
|
|
22733
22739
|
this.rrwebEventsLoaded = false;
|
|
22734
22740
|
this.loadRRwebEvents = [];
|
|
22735
22741
|
this.rrwebCrossFrameEventIdCounter = 0;
|
|
22742
|
+
this.firstRequestedIndex = void 0;
|
|
22736
22743
|
}
|
|
22737
22744
|
static {
|
|
22738
22745
|
__name(this, "RrwebEventsStorageManager");
|
|
22739
22746
|
}
|
|
22740
22747
|
async initRRwebEvents() {
|
|
22741
|
-
if (this.initialized) {
|
|
22742
|
-
console.warn(
|
|
22743
|
-
"[RrwebEventsStorageManager] initRRwebEvents called more than once"
|
|
22744
|
-
);
|
|
22745
|
-
return;
|
|
22746
|
-
}
|
|
22747
|
-
this.initialized = true;
|
|
22748
|
-
this.rrwebEventsIndexedDB = new IndexedDBClient("checksum", "rrwebEvents");
|
|
22749
22748
|
try {
|
|
22750
|
-
|
|
22749
|
+
if (this.initialized) {
|
|
22750
|
+
console.warn(
|
|
22751
|
+
"[RrwebEventsStorageManager] initRRwebEvents called more than once"
|
|
22752
|
+
);
|
|
22753
|
+
return;
|
|
22754
|
+
}
|
|
22755
|
+
this.initialized = true;
|
|
22756
|
+
this.rrwebEventsIndexedDB = new IndexedDBClient(
|
|
22757
|
+
"checksum",
|
|
22758
|
+
"rrwebEvents"
|
|
22759
|
+
);
|
|
22760
|
+
try {
|
|
22761
|
+
await this.rrwebEventsIndexedDB.open();
|
|
22762
|
+
} catch (e2) {
|
|
22763
|
+
return;
|
|
22764
|
+
}
|
|
22765
|
+
this.rrwebCrossFrameEventIdCounter = await this.rrwebEventsIndexedDB.count();
|
|
22766
|
+
this.loadRRwebEvents.forEach((event) => {
|
|
22767
|
+
this.rrwebEventsIndexedDB.set(
|
|
22768
|
+
event,
|
|
22769
|
+
this.rrwebCrossFrameEventIdCounter++
|
|
22770
|
+
);
|
|
22771
|
+
});
|
|
22772
|
+
this.rrwebEventsLoaded = true;
|
|
22751
22773
|
} catch (e2) {
|
|
22752
|
-
|
|
22774
|
+
console.log(e2);
|
|
22753
22775
|
}
|
|
22754
|
-
this.rrwebCrossFrameEventIdCounter = await this.rrwebEventsIndexedDB.count();
|
|
22755
|
-
this.loadRRwebEvents.forEach((event) => {
|
|
22756
|
-
this.rrwebEventsIndexedDB.set(
|
|
22757
|
-
event,
|
|
22758
|
-
this.rrwebCrossFrameEventIdCounter++
|
|
22759
|
-
);
|
|
22760
|
-
});
|
|
22761
|
-
this.rrwebEventsLoaded = true;
|
|
22762
22776
|
}
|
|
22763
22777
|
onRRwebEvent(event) {
|
|
22764
22778
|
if (this.rrwebEventsLoaded) {
|
|
@@ -22792,14 +22806,24 @@
|
|
|
22792
22806
|
body: JSON.stringify(rrwebEvents)
|
|
22793
22807
|
});
|
|
22794
22808
|
}
|
|
22795
|
-
async getRRwebEvents(lowerBoundKey) {
|
|
22796
|
-
|
|
22809
|
+
async getRRwebEvents(lowerBoundKey, size) {
|
|
22810
|
+
try {
|
|
22811
|
+
if (this.firstRequestedIndex === void 0) {
|
|
22812
|
+
this.firstRequestedIndex = lowerBoundKey;
|
|
22813
|
+
}
|
|
22814
|
+
lowerBoundKey -= this.firstRequestedIndex;
|
|
22815
|
+
if (!this.rrwebEventsLoaded) {
|
|
22816
|
+
return [];
|
|
22817
|
+
}
|
|
22818
|
+
const bound = size ? IDBKeyRange.bound(lowerBoundKey, lowerBoundKey + size) : IDBKeyRange.lowerBound(lowerBoundKey);
|
|
22819
|
+
const events = await this.rrwebEventsIndexedDB.getAll(
|
|
22820
|
+
bound
|
|
22821
|
+
);
|
|
22822
|
+
return events;
|
|
22823
|
+
} catch (e2) {
|
|
22824
|
+
console.log(e2);
|
|
22797
22825
|
return [];
|
|
22798
22826
|
}
|
|
22799
|
-
const events = await this.rrwebEventsIndexedDB.getAll(
|
|
22800
|
-
IDBKeyRange.lowerBound(lowerBoundKey)
|
|
22801
|
-
);
|
|
22802
|
-
return events;
|
|
22803
22827
|
}
|
|
22804
22828
|
async getRRwebEvent(key) {
|
|
22805
22829
|
if (!this.rrwebEventsLoaded) {
|
|
@@ -24955,6 +24979,116 @@
|
|
|
24955
24979
|
}
|
|
24956
24980
|
};
|
|
24957
24981
|
|
|
24982
|
+
// src/lib/test-generator/files-observer.ts
|
|
24983
|
+
var FilesObserver = class {
|
|
24984
|
+
static {
|
|
24985
|
+
__name(this, "FilesObserver");
|
|
24986
|
+
}
|
|
24987
|
+
constructor(sessionMirror) {
|
|
24988
|
+
this.fileInputsWithListeners = /* @__PURE__ */ new Set();
|
|
24989
|
+
this.filesMap = {};
|
|
24990
|
+
this.observer = null;
|
|
24991
|
+
this.sessionMirror = sessionMirror;
|
|
24992
|
+
}
|
|
24993
|
+
// Get files for a specific input by rrwebId
|
|
24994
|
+
async getFilesByRrwebId(rrwebId) {
|
|
24995
|
+
const files = this.filesMap[rrwebId] ?? [];
|
|
24996
|
+
try {
|
|
24997
|
+
const filesAsBase64 = await Promise.all(
|
|
24998
|
+
files.map(async (file) => ({
|
|
24999
|
+
data: await convertFileToBase64(file),
|
|
25000
|
+
fileName: file.name
|
|
25001
|
+
}))
|
|
25002
|
+
);
|
|
25003
|
+
this.filesMap[rrwebId] = [];
|
|
25004
|
+
return filesAsBase64;
|
|
25005
|
+
} catch {
|
|
25006
|
+
console.log("Error while parsing files to base 64");
|
|
25007
|
+
}
|
|
25008
|
+
}
|
|
25009
|
+
// Initialize the observer to track changes in the window
|
|
25010
|
+
init() {
|
|
25011
|
+
console.log("Starting observation...");
|
|
25012
|
+
console.log(this.sessionMirror);
|
|
25013
|
+
this.observeDOMChanges();
|
|
25014
|
+
window.addEventListener("unload", this.cleanup.bind(this));
|
|
25015
|
+
}
|
|
25016
|
+
// Handle the file input change event
|
|
25017
|
+
handleFileChange(event) {
|
|
25018
|
+
console.log("handleFileChange", this.sessionMirror);
|
|
25019
|
+
const target = event.target;
|
|
25020
|
+
const newFiles = Array.from(target.files || []);
|
|
25021
|
+
const rrwebMetaData = this.sessionMirror.getMeta(target);
|
|
25022
|
+
this.filesMap[rrwebMetaData.id] = newFiles;
|
|
25023
|
+
console.log(`Files updated for ID: ${rrwebMetaData.id}`, newFiles);
|
|
25024
|
+
}
|
|
25025
|
+
// Add listeners to file input elements
|
|
25026
|
+
addFileInputListeners(fileInputs) {
|
|
25027
|
+
fileInputs.forEach((input) => {
|
|
25028
|
+
if (!this.fileInputsWithListeners.has(input)) {
|
|
25029
|
+
input.addEventListener("change", this.handleFileChange.bind(this));
|
|
25030
|
+
this.fileInputsWithListeners.add(input);
|
|
25031
|
+
}
|
|
25032
|
+
});
|
|
25033
|
+
}
|
|
25034
|
+
// Observe DOM changes in the window's document
|
|
25035
|
+
observeDOMChanges() {
|
|
25036
|
+
const document2 = window.document;
|
|
25037
|
+
const initialFileInputs = document2.querySelectorAll(
|
|
25038
|
+
'input[type="file"]'
|
|
25039
|
+
);
|
|
25040
|
+
this.addFileInputListeners(initialFileInputs);
|
|
25041
|
+
const onBodyReady = /* @__PURE__ */ __name(() => {
|
|
25042
|
+
this.observer = new MutationObserver((mutationsList) => {
|
|
25043
|
+
mutationsList.forEach((mutation) => {
|
|
25044
|
+
if (mutation.type === "childList") {
|
|
25045
|
+
const fileInputs = mutation.target.querySelectorAll(
|
|
25046
|
+
'input[type="file"]'
|
|
25047
|
+
);
|
|
25048
|
+
this.addFileInputListeners(fileInputs);
|
|
25049
|
+
}
|
|
25050
|
+
});
|
|
25051
|
+
});
|
|
25052
|
+
this.observer.observe(document2.body, { childList: true, subtree: true });
|
|
25053
|
+
}, "onBodyReady");
|
|
25054
|
+
const bodyObserver = new MutationObserver(function() {
|
|
25055
|
+
if (document2.body) {
|
|
25056
|
+
bodyObserver.disconnect();
|
|
25057
|
+
onBodyReady();
|
|
25058
|
+
}
|
|
25059
|
+
});
|
|
25060
|
+
bodyObserver.observe(document2.documentElement, { childList: true });
|
|
25061
|
+
}
|
|
25062
|
+
// Cleanup the observer and remove event listeners
|
|
25063
|
+
cleanup() {
|
|
25064
|
+
console.log("Cleaning up files observer...");
|
|
25065
|
+
if (this.observer) {
|
|
25066
|
+
this.observer.disconnect();
|
|
25067
|
+
}
|
|
25068
|
+
this.fileInputsWithListeners.forEach((input) => {
|
|
25069
|
+
input.removeEventListener("change", this.handleFileChange);
|
|
25070
|
+
});
|
|
25071
|
+
this.fileInputsWithListeners.clear();
|
|
25072
|
+
this.filesMap = {};
|
|
25073
|
+
}
|
|
25074
|
+
};
|
|
25075
|
+
var convertFileToBase64 = /* @__PURE__ */ __name((file) => {
|
|
25076
|
+
return new Promise((resolve, reject) => {
|
|
25077
|
+
const reader = new FileReader();
|
|
25078
|
+
reader.onloadend = () => {
|
|
25079
|
+
if (reader.result) {
|
|
25080
|
+
resolve(reader.result.toString());
|
|
25081
|
+
} else {
|
|
25082
|
+
reject(new Error("File reading failed"));
|
|
25083
|
+
}
|
|
25084
|
+
};
|
|
25085
|
+
reader.onerror = () => {
|
|
25086
|
+
reject(new Error("File reading failed"));
|
|
25087
|
+
};
|
|
25088
|
+
reader.readAsDataURL(file);
|
|
25089
|
+
});
|
|
25090
|
+
}, "convertFileToBase64");
|
|
25091
|
+
|
|
24958
25092
|
// src/lib/test-generator/test-generator.ts
|
|
24959
25093
|
var LOGS_PREFIX = "$checksum";
|
|
24960
25094
|
var ChecksumTestGenerator = class {
|
|
@@ -25066,7 +25200,8 @@
|
|
|
25066
25200
|
}
|
|
25067
25201
|
// -------- [API] -------- //
|
|
25068
25202
|
init(appSpecificRules, config = {}, {
|
|
25069
|
-
sessionRecorder: initSessionRecorder = true
|
|
25203
|
+
sessionRecorder: initSessionRecorder = true,
|
|
25204
|
+
filesObserver: initFilesObserver = false
|
|
25070
25205
|
} = {}, options = {}) {
|
|
25071
25206
|
this.appSpecificRules = appSpecificRules;
|
|
25072
25207
|
this.config = config;
|
|
@@ -25078,10 +25213,20 @@
|
|
|
25078
25213
|
}
|
|
25079
25214
|
if (initSessionRecorder) {
|
|
25080
25215
|
this.sessionMirror = new SessionRecorder((event) => {
|
|
25216
|
+
console.log("send events");
|
|
25217
|
+
window.checksumSendMessage?.("vtg", { type: "event", data: event });
|
|
25218
|
+
try {
|
|
25219
|
+
window["onRrwebEvents"]?.([event]);
|
|
25220
|
+
} catch (e2) {
|
|
25221
|
+
}
|
|
25081
25222
|
rrwebEventsStorageManager.onRRwebEvent(event);
|
|
25082
25223
|
});
|
|
25083
25224
|
rrwebEventsStorageManager.initRRwebEvents();
|
|
25084
25225
|
}
|
|
25226
|
+
if (initFilesObserver) {
|
|
25227
|
+
this.filesObserver = new FilesObserver(this.sessionMirror);
|
|
25228
|
+
this.filesObserver.init();
|
|
25229
|
+
}
|
|
25085
25230
|
if (this.sessionMirror) {
|
|
25086
25231
|
this.sessionMirror.start();
|
|
25087
25232
|
this.htmlReducer.setSessionRecorder(this.sessionMirror);
|
|
@@ -25904,8 +26049,13 @@
|
|
|
25904
26049
|
super.setEventHandlers(eventHandlers);
|
|
25905
26050
|
}
|
|
25906
26051
|
async handleEvents(events, len) {
|
|
25907
|
-
console.log("starting handleEvents", len);
|
|
25908
26052
|
try {
|
|
26053
|
+
events = events.filter(
|
|
26054
|
+
(event) => event.timestamp >= (this.lastSeenTimestamp ? this.lastSeenTimestamp : 0)
|
|
26055
|
+
);
|
|
26056
|
+
if (events.length) {
|
|
26057
|
+
this.lastSeenTimestamp = events[events.length - 1].timestamp;
|
|
26058
|
+
}
|
|
25909
26059
|
for (const event of events) {
|
|
25910
26060
|
await this.eventProcessor.processEvent(event, this.shouldHandleEvents);
|
|
25911
26061
|
await this.skipEvents([event]);
|
|
@@ -26138,6 +26288,7 @@
|
|
|
26138
26288
|
constructor() {
|
|
26139
26289
|
super();
|
|
26140
26290
|
this.events = [];
|
|
26291
|
+
this.previousEvent = null;
|
|
26141
26292
|
this.interactions = [];
|
|
26142
26293
|
this.lastInteractionEventIndex = 0;
|
|
26143
26294
|
this.sequences = [];
|
|
@@ -26186,7 +26337,9 @@
|
|
|
26186
26337
|
if (!draggableNode) {
|
|
26187
26338
|
return;
|
|
26188
26339
|
}
|
|
26189
|
-
const draggableSelector = await this.elementSelector.getSelector(
|
|
26340
|
+
const draggableSelector = await this.elementSelector.getSelector(
|
|
26341
|
+
draggableNode
|
|
26342
|
+
);
|
|
26190
26343
|
if (!draggableSelector) {
|
|
26191
26344
|
return;
|
|
26192
26345
|
}
|
|
@@ -26196,7 +26349,9 @@
|
|
|
26196
26349
|
if (!dropzoneNode) {
|
|
26197
26350
|
return;
|
|
26198
26351
|
}
|
|
26199
|
-
const dropzoneSelector = await this.elementSelector.getSelector(
|
|
26352
|
+
const dropzoneSelector = await this.elementSelector.getSelector(
|
|
26353
|
+
dropzoneNode
|
|
26354
|
+
);
|
|
26200
26355
|
if (!dropzoneSelector) {
|
|
26201
26356
|
return;
|
|
26202
26357
|
}
|
|
@@ -26247,6 +26402,7 @@
|
|
|
26247
26402
|
this.events.push(event);
|
|
26248
26403
|
}
|
|
26249
26404
|
async postEvent(event, result) {
|
|
26405
|
+
this.previousEvent = event;
|
|
26250
26406
|
try {
|
|
26251
26407
|
await this.performSequenceSearch();
|
|
26252
26408
|
} catch (e2) {
|
|
@@ -26276,7 +26432,9 @@
|
|
|
26276
26432
|
event,
|
|
26277
26433
|
selector
|
|
26278
26434
|
);
|
|
26435
|
+
action.rrwebId = data.id;
|
|
26279
26436
|
if (action.eventCode === "input" /* Input */) {
|
|
26437
|
+
action.timestamp = this.previousEvent?.timestamp ?? event.timestamp;
|
|
26280
26438
|
action.fillValue = data.text;
|
|
26281
26439
|
this.inputFilter = {
|
|
26282
26440
|
action,
|
|
@@ -26288,7 +26446,7 @@
|
|
|
26288
26446
|
}
|
|
26289
26447
|
if (action.eventCode === "upload_files" /* UploadFiles */) {
|
|
26290
26448
|
action.files = [];
|
|
26291
|
-
action.files.push(data.text);
|
|
26449
|
+
action.files.push(data.text.split("\\").pop());
|
|
26292
26450
|
}
|
|
26293
26451
|
this.addInteraction(action);
|
|
26294
26452
|
}
|
|
@@ -26421,6 +26579,9 @@
|
|
|
26421
26579
|
}
|
|
26422
26580
|
}
|
|
26423
26581
|
addInteraction(action) {
|
|
26582
|
+
if (!action.id) {
|
|
26583
|
+
action.id = Date.now().toString();
|
|
26584
|
+
}
|
|
26424
26585
|
this.interactions.push(action);
|
|
26425
26586
|
this.lastInteractionEventIndex = this.events.length;
|
|
26426
26587
|
}
|
|
@@ -26485,6 +26646,9 @@
|
|
|
26485
26646
|
this.singleSelection = true;
|
|
26486
26647
|
this.subDocumentInspector = null;
|
|
26487
26648
|
this.listening = false;
|
|
26649
|
+
this.onMouseOut = /* @__PURE__ */ __name((event) => {
|
|
26650
|
+
elementHighlighter.clearHighlights();
|
|
26651
|
+
}, "onMouseOut");
|
|
26488
26652
|
this.onMouseOver = /* @__PURE__ */ __name(async (event) => {
|
|
26489
26653
|
const target = event.composedPath()[0];
|
|
26490
26654
|
if ("getRootNode" in target) {
|
|
@@ -26556,6 +26720,13 @@
|
|
|
26556
26720
|
if (this.singleSelection) {
|
|
26557
26721
|
this.topLevelInspector ? this.topLevelInspector.stop() : this.stop();
|
|
26558
26722
|
}
|
|
26723
|
+
window.parent.postMessage(
|
|
26724
|
+
{
|
|
26725
|
+
type: "inspector-selection",
|
|
26726
|
+
data: this.selected.at(this.selected.length - 1)
|
|
26727
|
+
},
|
|
26728
|
+
"*"
|
|
26729
|
+
);
|
|
26559
26730
|
console.log("selected", this.selected);
|
|
26560
26731
|
}, "onClick");
|
|
26561
26732
|
this.handleSubDocument = /* @__PURE__ */ __name((newRootDocument, defaultView) => {
|
|
@@ -26592,6 +26763,7 @@
|
|
|
26592
26763
|
this.stop();
|
|
26593
26764
|
this.cleanSelection();
|
|
26594
26765
|
this.rootDocument.addEventListener("mouseover", this.onMouseOver);
|
|
26766
|
+
this.rootDocument.addEventListener("mouseout", this.onMouseOut);
|
|
26595
26767
|
this.listening = true;
|
|
26596
26768
|
}
|
|
26597
26769
|
stop(clean = false) {
|
|
@@ -26641,14 +26813,18 @@
|
|
|
26641
26813
|
static {
|
|
26642
26814
|
__name(this, "VisualTestGenerator");
|
|
26643
26815
|
}
|
|
26644
|
-
init(
|
|
26645
|
-
this.timeMachine.setEventHandlers(this.eventHandlers,
|
|
26816
|
+
init() {
|
|
26817
|
+
this.timeMachine.setEventHandlers(this.eventHandlers, true);
|
|
26646
26818
|
}
|
|
26647
26819
|
consumeInteractions() {
|
|
26648
26820
|
const interactions = this.eventHandlers.getInteractions(
|
|
26649
26821
|
this.lengthOfConsumedInteractions
|
|
26650
26822
|
);
|
|
26651
26823
|
this.lengthOfConsumedInteractions += interactions.length;
|
|
26824
|
+
window.checksumSendMessage("vtg", {
|
|
26825
|
+
type: "consume-interactions",
|
|
26826
|
+
data: interactions
|
|
26827
|
+
});
|
|
26652
26828
|
return interactions;
|
|
26653
26829
|
}
|
|
26654
26830
|
startInspector(singleSelection = true, rootDocument) {
|
|
@@ -26661,7 +26837,7 @@
|
|
|
26661
26837
|
return document.querySelector(".replayer-wrapper > iframe").contentDocument;
|
|
26662
26838
|
}
|
|
26663
26839
|
stopInspector() {
|
|
26664
|
-
this.elementInspector
|
|
26840
|
+
this.elementInspector?.stop();
|
|
26665
26841
|
}
|
|
26666
26842
|
consumeSelections() {
|
|
26667
26843
|
return this.elementInspector.consumeSelections();
|