@memlab/e2e 1.0.0

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.
Files changed (118) hide show
  1. package/README.md +11 -0
  2. package/dist/BaseSynthesizer.d.ts +63 -0
  3. package/dist/BaseSynthesizer.d.ts.map +1 -0
  4. package/dist/BaseSynthesizer.js +433 -0
  5. package/dist/E2EInteractionManager.d.ts +41 -0
  6. package/dist/E2EInteractionManager.d.ts.map +1 -0
  7. package/dist/E2EInteractionManager.js +347 -0
  8. package/dist/TestRunnerLoader.d.ts +16 -0
  9. package/dist/TestRunnerLoader.d.ts.map +1 -0
  10. package/dist/TestRunnerLoader.js +41 -0
  11. package/dist/TypesThirdParty.d.ts +25 -0
  12. package/dist/TypesThirdParty.d.ts.map +1 -0
  13. package/dist/TypesThirdParty.js +10 -0
  14. package/dist/index.d.ts +16 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +40 -0
  17. package/dist/lib/E2EUtils.d.ts +59 -0
  18. package/dist/lib/E2EUtils.d.ts.map +1 -0
  19. package/dist/lib/E2EUtils.js +286 -0
  20. package/dist/lib/SynthesisUtils.d.ts +25 -0
  21. package/dist/lib/SynthesisUtils.d.ts.map +1 -0
  22. package/dist/lib/SynthesisUtils.js +30 -0
  23. package/dist/lib/operations/BackOperation.d.ts +17 -0
  24. package/dist/lib/operations/BackOperation.d.ts.map +1 -0
  25. package/dist/lib/operations/BackOperation.js +37 -0
  26. package/dist/lib/operations/BackspaceOperation.d.ts +18 -0
  27. package/dist/lib/operations/BackspaceOperation.d.ts.map +1 -0
  28. package/dist/lib/operations/BackspaceOperation.js +36 -0
  29. package/dist/lib/operations/BaseOperation.d.ts +22 -0
  30. package/dist/lib/operations/BaseOperation.d.ts.map +1 -0
  31. package/dist/lib/operations/BaseOperation.js +67 -0
  32. package/dist/lib/operations/CachePageContent.d.ts +22 -0
  33. package/dist/lib/operations/CachePageContent.d.ts.map +1 -0
  34. package/dist/lib/operations/CachePageContent.js +46 -0
  35. package/dist/lib/operations/ClickOperation.d.ts +36 -0
  36. package/dist/lib/operations/ClickOperation.d.ts.map +1 -0
  37. package/dist/lib/operations/ClickOperation.js +131 -0
  38. package/dist/lib/operations/CompoundOperation.d.ts +20 -0
  39. package/dist/lib/operations/CompoundOperation.d.ts.map +1 -0
  40. package/dist/lib/operations/CompoundOperation.js +39 -0
  41. package/dist/lib/operations/EnterOperation.d.ts +18 -0
  42. package/dist/lib/operations/EnterOperation.d.ts.map +1 -0
  43. package/dist/lib/operations/EnterOperation.js +36 -0
  44. package/dist/lib/operations/EscOperation.d.ts +17 -0
  45. package/dist/lib/operations/EscOperation.d.ts.map +1 -0
  46. package/dist/lib/operations/EscOperation.js +36 -0
  47. package/dist/lib/operations/HoverOperation.d.ts +28 -0
  48. package/dist/lib/operations/HoverOperation.d.ts.map +1 -0
  49. package/dist/lib/operations/HoverOperation.js +73 -0
  50. package/dist/lib/operations/InteractionUtils.d.ts +33 -0
  51. package/dist/lib/operations/InteractionUtils.d.ts.map +1 -0
  52. package/dist/lib/operations/InteractionUtils.js +207 -0
  53. package/dist/lib/operations/Operations.d.ts +25 -0
  54. package/dist/lib/operations/Operations.d.ts.map +1 -0
  55. package/dist/lib/operations/Operations.js +45 -0
  56. package/dist/lib/operations/RevertOperation.d.ts +18 -0
  57. package/dist/lib/operations/RevertOperation.d.ts.map +1 -0
  58. package/dist/lib/operations/RevertOperation.js +55 -0
  59. package/dist/lib/operations/RunJSCode.d.ts +25 -0
  60. package/dist/lib/operations/RunJSCode.d.ts.map +1 -0
  61. package/dist/lib/operations/RunJSCode.js +51 -0
  62. package/dist/lib/operations/ScrollOperation.d.ts +27 -0
  63. package/dist/lib/operations/ScrollOperation.d.ts.map +1 -0
  64. package/dist/lib/operations/ScrollOperation.js +85 -0
  65. package/dist/lib/operations/TargetExtraOperation.d.ts +18 -0
  66. package/dist/lib/operations/TargetExtraOperation.d.ts.map +1 -0
  67. package/dist/lib/operations/TargetExtraOperation.js +28 -0
  68. package/dist/lib/operations/TestPlanner.d.ts +46 -0
  69. package/dist/lib/operations/TestPlanner.d.ts.map +1 -0
  70. package/dist/lib/operations/TestPlanner.js +182 -0
  71. package/dist/lib/operations/TypeOperation.d.ts +29 -0
  72. package/dist/lib/operations/TypeOperation.d.ts.map +1 -0
  73. package/dist/lib/operations/TypeOperation.js +64 -0
  74. package/dist/lib/operations/UploadOperation.d.ts +25 -0
  75. package/dist/lib/operations/UploadOperation.d.ts.map +1 -0
  76. package/dist/lib/operations/UploadOperation.js +56 -0
  77. package/dist/lib/operations/WaitForElementOperation.d.ts +32 -0
  78. package/dist/lib/operations/WaitForElementOperation.d.ts.map +1 -0
  79. package/dist/lib/operations/WaitForElementOperation.js +67 -0
  80. package/dist/lib/operations/WaitOperation.d.ts +19 -0
  81. package/dist/lib/operations/WaitOperation.d.ts.map +1 -0
  82. package/dist/lib/operations/WaitOperation.js +42 -0
  83. package/dist/lib/operations/WithCachedPageContent.d.ts +26 -0
  84. package/dist/lib/operations/WithCachedPageContent.d.ts.map +1 -0
  85. package/dist/lib/operations/WithCachedPageContent.js +53 -0
  86. package/dist/lib/operations/XVirtualFrameBuffer.d.ts +15 -0
  87. package/dist/lib/operations/XVirtualFrameBuffer.d.ts.map +1 -0
  88. package/dist/lib/operations/XVirtualFrameBuffer.js +44 -0
  89. package/dist/plugins/DefaultScenarioSynthesizer.d.ts +23 -0
  90. package/dist/plugins/DefaultScenarioSynthesizer.d.ts.map +1 -0
  91. package/dist/plugins/DefaultScenarioSynthesizer.js +61 -0
  92. package/dist/plugins/TestSPAVisitSynthesizer.d.ts +23 -0
  93. package/dist/plugins/TestSPAVisitSynthesizer.d.ts.map +1 -0
  94. package/dist/plugins/TestSPAVisitSynthesizer.js +72 -0
  95. package/dist/plugins/scenarios/test-airbnb.d.ts +14 -0
  96. package/dist/plugins/scenarios/test-airbnb.d.ts.map +1 -0
  97. package/dist/plugins/scenarios/test-airbnb.js +48 -0
  98. package/dist/plugins/scenarios/test-databricks.d.ts +14 -0
  99. package/dist/plugins/scenarios/test-databricks.d.ts.map +1 -0
  100. package/dist/plugins/scenarios/test-databricks.js +40 -0
  101. package/dist/plugins/scenarios/test-ebay.d.ts +14 -0
  102. package/dist/plugins/scenarios/test-ebay.d.ts.map +1 -0
  103. package/dist/plugins/scenarios/test-ebay.js +48 -0
  104. package/dist/plugins/scenarios/test-google-maps.d.ts +14 -0
  105. package/dist/plugins/scenarios/test-google-maps.d.ts.map +1 -0
  106. package/dist/plugins/scenarios/test-google-maps.js +41 -0
  107. package/dist/plugins/scenarios/test-shopify.d.ts +14 -0
  108. package/dist/plugins/scenarios/test-shopify.d.ts.map +1 -0
  109. package/dist/plugins/scenarios/test-shopify.js +40 -0
  110. package/dist/plugins/scenarios/test-youtube.d.ts +14 -0
  111. package/dist/plugins/scenarios/test-youtube.d.ts.map +1 -0
  112. package/dist/plugins/scenarios/test-youtube.js +40 -0
  113. package/package.json +53 -0
  114. package/static/links/index.html +18 -0
  115. package/static/links/min.js +3 -0
  116. package/static/links/package.json +36 -0
  117. package/static/links/src/App.js +48 -0
  118. package/static/links/src/index.js +20 -0
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # `@memlab/e2e`
2
+
3
+ > TODO: description
4
+
5
+ ## Usage
6
+
7
+ ```
8
+ const e2e = require('e2e');
9
+
10
+ // TODO: DEMONSTRATE API
11
+ ```
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @emails oncall+ws_labs
8
+ * @lightSyntaxTransform
9
+ * @format
10
+ */
11
+ import type { AnyOptions, Config, E2EOperation, E2EStepInfo, IE2EScenarioVisitPlan, E2ESynthesizerOptions, IScenario, IE2EStepBasic, IE2EScenarioSynthesizer, CheckPageLoadCallback, Nullable } from '@memlab/core';
12
+ declare class BaseSynthesizer implements IE2EScenarioSynthesizer {
13
+ protected config: Config;
14
+ protected steps: Map<string, IE2EStepBasic>;
15
+ private _repeatIntermediate;
16
+ protected plans: Map<string, IE2EScenarioVisitPlan>;
17
+ constructor(config: Config);
18
+ private injectBuiltInSteps;
19
+ private repeatIntermediate;
20
+ protected init(): void;
21
+ protected postInit(): void;
22
+ getAppName(): string;
23
+ getOrigin(): Nullable<string>;
24
+ getDomain(): string;
25
+ getDomainPrefixes(): string[];
26
+ getCookieFile(_visitPlan: IE2EScenarioVisitPlan): string | null;
27
+ getAvailableSteps(): IE2EStepBasic[];
28
+ getNodeNameBlocklist(): string[];
29
+ getEdgeNameBlocklist(): string[];
30
+ getDefaultStartStepName(): string;
31
+ injectPlan(stepName: string, scenario: IScenario): void;
32
+ synthesisForTarget(stepName: string): IE2EScenarioVisitPlan;
33
+ getAvailableVisitPlans(): IE2EScenarioVisitPlan[];
34
+ getAvailableTargetNames(opt?: AnyOptions & {
35
+ filterCb?: (node: IE2EScenarioVisitPlan) => boolean;
36
+ }): string[];
37
+ getNumberOfWarmup(): number;
38
+ getBaseURL(_options?: AnyOptions): string;
39
+ private getNormalizedBaseURL;
40
+ getURLParameters(_options?: AnyOptions): string;
41
+ getDevice(): string;
42
+ getExtraOperationsForStep(_stepInfo: E2EStepInfo): E2EOperation[];
43
+ getPageLoadChecker(): CheckPageLoadCallback;
44
+ synthesis(baseline: string, target: string, intermediates: string[], options?: E2ESynthesizerOptions): IE2EScenarioVisitPlan;
45
+ private getActualNumberOfWarmup;
46
+ private synthesisInitPart;
47
+ private synthesisFinalPart;
48
+ private synthesisRepeat;
49
+ private synthesisStandard;
50
+ private processStepUrl;
51
+ private synthesisForScenario;
52
+ private synthesisSimple;
53
+ private synthesisInitialPageLoad;
54
+ private getActionFromStep;
55
+ private getAction;
56
+ protected shouldTakeSnapshot(option: AnyOptions & {
57
+ type?: string;
58
+ }): boolean;
59
+ protected shouldTakeScreenshot(_option: AnyOptions): boolean;
60
+ private fillInInfo;
61
+ }
62
+ export default BaseSynthesizer;
63
+ //# sourceMappingURL=BaseSynthesizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseSynthesizer.d.ts","sourceRoot":"","sources":["../src/BaseSynthesizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EACV,UAAU,EACV,MAAM,EACN,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,qBAAqB,EACrB,SAAS,EACT,aAAa,EAEb,uBAAuB,EACvB,qBAAqB,EACrB,QAAQ,EACT,MAAM,cAAc,CAAC;AAWtB,cAAM,eAAgB,YAAW,uBAAuB;IACtD,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC5C,OAAO,CAAC,mBAAmB,CAAS;IACpC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;gBAExC,MAAM,EAAE,MAAM;IAkB1B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,kBAAkB;IAI1B,SAAS,CAAC,IAAI,IAAI,IAAI;IAItB,SAAS,CAAC,QAAQ,IAAI,IAAI;IAI1B,UAAU,IAAI,MAAM;IAMpB,SAAS,IAAI,QAAQ,CAAC,MAAM,CAAC;IAW7B,SAAS,IAAI,MAAM;IAMnB,iBAAiB,IAAI,MAAM,EAAE;IAK7B,aAAa,CAAC,UAAU,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI;IAM/D,iBAAiB,IAAI,aAAa,EAAE;IAQpC,oBAAoB,IAAI,MAAM,EAAE;IAIhC,oBAAoB,IAAI,MAAM,EAAE;IAIhC,uBAAuB,IAAI,MAAM;IAQjC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI;IAKvD,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB;IAQ3D,sBAAsB,IAAI,qBAAqB,EAAE;IAQjD,uBAAuB,CACrB,GAAG,GAAE,UAAU,GAAG;QAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC;KAChD,GACL,MAAM,EAAE;IAUX,iBAAiB,IAAI,MAAM;IAM3B,UAAU,CAAC,QAAQ,GAAE,UAAe;IAIpC,OAAO,CAAC,oBAAoB;IAO5B,gBAAgB,CAAC,QAAQ,GAAE,UAAe;IAM1C,SAAS,IAAI,MAAM;IAMnB,yBAAyB,CAAC,SAAS,EAAE,WAAW,GAAG,YAAY,EAAE;IAKjE,kBAAkB,IAAI,qBAAqB;IAQ3C,SAAS,CACP,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EAAE,EACvB,OAAO,GAAE,qBAA0B,GAClC,qBAAqB;IAiBxB,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,iBAAiB;IA6BzB,OAAO,CAAC,kBAAkB;IA2B1B,OAAO,CAAC,eAAe;IAkDvB,OAAO,CAAC,iBAAiB;IA6BzB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,wBAAwB;IAuChC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,SAAS;IAcjB,SAAS,CAAC,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO;IAO3E,SAAS,CAAC,oBAAoB,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO;IAI5D,OAAO,CAAC,UAAU;CAoCnB;AAED,eAAe,eAAe,CAAC"}
@@ -0,0 +1,433 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const core_1 = require("@memlab/core");
16
+ const SynthesisUtils_1 = __importDefault(require("./lib/SynthesisUtils"));
17
+ const InteractionUtils_1 = __importDefault(require("./lib/operations/InteractionUtils"));
18
+ const E2EUtils_1 = __importDefault(require("./lib/E2EUtils"));
19
+ // for debugging only
20
+ const skipAllScreenshot = false;
21
+ class BaseSynthesizer {
22
+ constructor(config) {
23
+ this.config = config;
24
+ this.init();
25
+ this.postInit();
26
+ // define steps
27
+ this.steps = new Map();
28
+ for (const step of this.getAvailableSteps()) {
29
+ this.steps.set(step.name, step);
30
+ }
31
+ this.injectBuiltInSteps();
32
+ this._repeatIntermediate = 1;
33
+ // define visit plans
34
+ this.plans = new Map();
35
+ for (const plan of this.getAvailableVisitPlans()) {
36
+ this.plans.set(plan.name, plan);
37
+ }
38
+ }
39
+ injectBuiltInSteps() {
40
+ const backStep = SynthesisUtils_1.default.backStep;
41
+ this.steps.set(backStep.name, backStep);
42
+ const revertStep = SynthesisUtils_1.default.revertStep;
43
+ this.steps.set(revertStep.name, revertStep);
44
+ }
45
+ repeatIntermediate(n) {
46
+ this._repeatIntermediate = n;
47
+ }
48
+ init() {
49
+ this.repeatIntermediate(this.config.repeatIntermediateTabs);
50
+ }
51
+ postInit() {
52
+ // for overriding
53
+ }
54
+ getAppName() {
55
+ const error = new Error('BaseSynthesizer.getAppName is not implemented');
56
+ error.stack;
57
+ throw error;
58
+ }
59
+ getOrigin() {
60
+ const domain = this.getDomain();
61
+ if (domain === '' || domain == null) {
62
+ return null;
63
+ }
64
+ if (domain.startsWith('.')) {
65
+ return `https://${domain.substring(1)}`;
66
+ }
67
+ return `https://${domain}`;
68
+ }
69
+ getDomain() {
70
+ const error = new Error('BaseSynthesizer.getDomain is not implemented');
71
+ error.stack;
72
+ throw error;
73
+ }
74
+ getDomainPrefixes() {
75
+ return [];
76
+ }
77
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
78
+ getCookieFile(_visitPlan) {
79
+ const error = new Error('BaseSynthesizer.getCookieFile is not implemented');
80
+ error.stack;
81
+ throw error;
82
+ }
83
+ getAvailableSteps() {
84
+ const error = new Error('BaseSynthesizer.getAvailableSteps is not implemented');
85
+ error.stack;
86
+ throw error;
87
+ }
88
+ getNodeNameBlocklist() {
89
+ return [];
90
+ }
91
+ getEdgeNameBlocklist() {
92
+ return [];
93
+ }
94
+ getDefaultStartStepName() {
95
+ const error = new Error('BaseSynthesizer.getDefaultStartStepName is not implemented');
96
+ error.stack;
97
+ throw error;
98
+ }
99
+ injectPlan(stepName, scenario) {
100
+ this.plans.set(stepName, this.synthesisForScenario(scenario));
101
+ }
102
+ // given a target step name, returns the visit order sequence
103
+ synthesisForTarget(stepName) {
104
+ if (!this.plans.has(stepName)) {
105
+ core_1.utils.haltOrThrow(`target ${stepName} is unknown to synthesizers`);
106
+ }
107
+ return this.plans.get(stepName);
108
+ }
109
+ // return a list of visit plans for the target app
110
+ getAvailableVisitPlans() {
111
+ const error = new Error('BaseSynthesizer.getAvailableVisitPlans is not implemented');
112
+ error.stack;
113
+ throw error;
114
+ }
115
+ getAvailableTargetNames(opt = {}) {
116
+ const filterCb = opt.filterCb;
117
+ let plans = Array.from(this.plans.values());
118
+ if (filterCb) {
119
+ plans = plans.filter(filterCb);
120
+ }
121
+ return plans.map(plan => plan.name);
122
+ }
123
+ // get the default number of warmups
124
+ getNumberOfWarmup() {
125
+ return 1;
126
+ }
127
+ // get the URL of the target app
128
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types
129
+ getBaseURL(_options = {}) {
130
+ return 'https://www.facebook.com';
131
+ }
132
+ getNormalizedBaseURL(options = {}) {
133
+ const url = core_1.utils.normalizeBaseUrl(this.getBaseURL(options));
134
+ return url;
135
+ }
136
+ // get URL parameters
137
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types
138
+ getURLParameters(_options = {}) {
139
+ return '';
140
+ }
141
+ // get default (mobile) device name
142
+ // if nothing specified, it's considered running in desktop / laptop
143
+ getDevice() {
144
+ return 'pc';
145
+ }
146
+ // append additional interactions based on step info
147
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
148
+ getExtraOperationsForStep(_stepInfo) {
149
+ return [];
150
+ }
151
+ // get default callback for checking page load
152
+ getPageLoadChecker() {
153
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
154
+ return (_page) => __awaiter(this, void 0, void 0, function* () {
155
+ yield InteractionUtils_1.default.waitFor(this.config.delayWhenNoPageLoadCheck);
156
+ return true;
157
+ });
158
+ }
159
+ synthesis(baseline, target, intermediates, options = {}) {
160
+ const type = options.type;
161
+ if (!type || type === 'standard') {
162
+ return this.synthesisStandard(baseline, target, intermediates, options);
163
+ }
164
+ if (type === 'simple') {
165
+ return this.synthesisSimple(baseline, target, options);
166
+ }
167
+ if (type === 'repeat') {
168
+ return this.synthesisRepeat(baseline, target, intermediates, options);
169
+ }
170
+ if (type === 'init-page-load') {
171
+ return this.synthesisInitialPageLoad(target, intermediates, options);
172
+ }
173
+ throw core_1.utils.haltOrThrow(`unknown visit plan type: ${type}`);
174
+ }
175
+ getActualNumberOfWarmup() {
176
+ return this.getNumberOfWarmup() + (this.config.isContinuousTest ? 1 : 0);
177
+ }
178
+ synthesisInitPart(baseline, target, options) {
179
+ // default start from the home tab
180
+ const start = options.start || this.getDefaultStartStepName();
181
+ const visitOrder = [];
182
+ // first visit the start step
183
+ visitOrder.push(this.getAction(start, 'init'));
184
+ // if provided, some initial setup to prepare states or data
185
+ if (options.setupSteps) {
186
+ for (const stepName of options.setupSteps) {
187
+ visitOrder.push(this.getAction(stepName));
188
+ }
189
+ }
190
+ // if provided, warm up with specified steps
191
+ if (options.warmupSteps) {
192
+ for (const stepName of options.warmupSteps) {
193
+ visitOrder.push(this.getAction(stepName));
194
+ }
195
+ }
196
+ else {
197
+ // by default, warm up the baseline and target onces
198
+ visitOrder.push(this.getAction(baseline));
199
+ visitOrder.push(this.getAction(target));
200
+ }
201
+ return visitOrder;
202
+ }
203
+ synthesisFinalPart(visitOrder, baseline, intermediates, options = {}) {
204
+ // visit intermediate steps twice
205
+ for (let i = 0; i < this._repeatIntermediate; i++) {
206
+ for (const step of intermediates) {
207
+ visitOrder.push(this.getAction(step));
208
+ }
209
+ }
210
+ // visit the final step
211
+ const final = options.final || baseline;
212
+ visitOrder.push(this.getAction(final, 'final'));
213
+ // if provided, some cleanup to restore states
214
+ if (options.cleanupSteps) {
215
+ for (const stepName of options.cleanupSteps) {
216
+ visitOrder.push(this.getAction(stepName));
217
+ }
218
+ }
219
+ }
220
+ // synthesis for a new type of memory stress testing
221
+ // that repeats a series of steps
222
+ synthesisRepeat(baseline, target, _, options = {}) {
223
+ const visitOrder = this.synthesisInitPart(baseline, target, options);
224
+ // visit baseline
225
+ visitOrder.push(this.getAction(baseline, 'baseline'));
226
+ let repeat = options.repeat != null ? options.repeat : this.config.stressTestRepeat;
227
+ if (options.repeatSteps) {
228
+ while (repeat-- > 0) {
229
+ for (const step of options.repeatSteps) {
230
+ visitOrder.push(this.getAction(step));
231
+ }
232
+ }
233
+ }
234
+ else {
235
+ while (repeat-- > 0) {
236
+ // repeatedly visit the baseline and target
237
+ visitOrder.push(this.getAction(target));
238
+ visitOrder.push(this.getAction(baseline));
239
+ }
240
+ }
241
+ visitOrder.push(this.getAction(target, 'target'));
242
+ // if provided, some cleanup to restore states
243
+ if (options.cleanupSteps) {
244
+ for (const stepName of options.cleanupSteps) {
245
+ visitOrder.push(this.getAction(stepName));
246
+ }
247
+ }
248
+ return {
249
+ name: options.name || `${baseline}-${target}`,
250
+ appName: this.getAppName(),
251
+ type: 'repeat',
252
+ newTestUser: !!options.newTestUser,
253
+ device: this.getDevice(),
254
+ baseURL: this.getNormalizedBaseURL(options),
255
+ URLParameters: this.getURLParameters(options),
256
+ tabsOrder: visitOrder,
257
+ numOfWarmup: this.getActualNumberOfWarmup(),
258
+ dataBuilder: options.dataBuilder,
259
+ isPageLoaded: this.getPageLoadChecker(),
260
+ };
261
+ }
262
+ synthesisStandard(baseline, target, intermediates, options = {}) {
263
+ const visitOrder = this.synthesisInitPart(baseline, target, options);
264
+ // actually visit the baseline and target
265
+ visitOrder.push(this.getAction(baseline, 'baseline'));
266
+ visitOrder.push(this.getAction(target, 'target'));
267
+ // intermediate steps, final step, and cleanup steps
268
+ this.synthesisFinalPart(visitOrder, baseline, intermediates, options);
269
+ return {
270
+ name: options.name || target,
271
+ appName: this.getAppName(),
272
+ type: 'standard',
273
+ newTestUser: !!options.newTestUser,
274
+ device: this.getDevice(),
275
+ baseURL: this.getNormalizedBaseURL(options),
276
+ URLParameters: this.getURLParameters(options),
277
+ tabsOrder: visitOrder,
278
+ numOfWarmup: this.getActualNumberOfWarmup(),
279
+ dataBuilder: options.dataBuilder,
280
+ isPageLoaded: this.getPageLoadChecker(),
281
+ };
282
+ }
283
+ processStepUrl(url) {
284
+ let ret = url;
285
+ while (ret.startsWith('/')) {
286
+ ret = ret.substring(1);
287
+ }
288
+ return ret;
289
+ }
290
+ synthesisForScenario(scenario) {
291
+ var _a;
292
+ const visitOrder = [];
293
+ const url = this.processStepUrl(scenario.url());
294
+ const startStep = {
295
+ name: 'page-load',
296
+ url,
297
+ interactions: [],
298
+ };
299
+ if (!scenario.action) {
300
+ visitOrder.push(this.getActionFromStep(startStep, 'target'));
301
+ }
302
+ else {
303
+ // first visit the start step
304
+ visitOrder.push(this.getActionFromStep(startStep, 'baseline'));
305
+ const actionStep = {
306
+ name: 'action-on-page',
307
+ url,
308
+ interactions: [scenario.action],
309
+ };
310
+ const revertStep = {
311
+ name: 'revert',
312
+ url,
313
+ interactions: [scenario.back],
314
+ };
315
+ const getRevertStep = (stepType) => scenario.back
316
+ ? this.getActionFromStep(revertStep, stepType)
317
+ : this.getAction(SynthesisUtils_1.default.revertStep.name, stepType);
318
+ const repeat = scenario.repeat ? scenario.repeat() : 0;
319
+ for (let i = 0; i < repeat; ++i) {
320
+ visitOrder.push(this.getActionFromStep(actionStep));
321
+ visitOrder.push(getRevertStep());
322
+ }
323
+ visitOrder.push(this.getActionFromStep(actionStep, 'target'));
324
+ visitOrder.push(getRevertStep('final'));
325
+ }
326
+ return {
327
+ name: core_1.utils.getScenarioName(scenario),
328
+ appName: E2EUtils_1.default.getScenarioAppName(scenario),
329
+ type: 'scenario',
330
+ newTestUser: false,
331
+ device: this.getDevice(),
332
+ baseURL: this.getNormalizedBaseURL(),
333
+ URLParameters: this.getURLParameters(),
334
+ tabsOrder: visitOrder,
335
+ numOfWarmup: this.getActualNumberOfWarmup(),
336
+ dataBuilder: null,
337
+ isPageLoaded: (_a = scenario.isPageLoaded) !== null && _a !== void 0 ? _a : this.getPageLoadChecker(),
338
+ };
339
+ }
340
+ synthesisSimple(start, target, options = {}) {
341
+ const visitOrder = [];
342
+ // first visit the start step
343
+ visitOrder.push(this.getAction(start, 'baseline'));
344
+ visitOrder.push(this.getAction(target, 'target'));
345
+ visitOrder.push(this.getAction(SynthesisUtils_1.default.revertStep.name, 'final'));
346
+ return {
347
+ name: options.name || target,
348
+ appName: this.getAppName(),
349
+ type: 'simple',
350
+ newTestUser: !!options.newTestUser,
351
+ device: this.getDevice(),
352
+ baseURL: this.getNormalizedBaseURL(options),
353
+ URLParameters: this.getURLParameters(options),
354
+ tabsOrder: visitOrder,
355
+ numOfWarmup: this.getActualNumberOfWarmup(),
356
+ dataBuilder: options.dataBuilder,
357
+ isPageLoaded: this.getPageLoadChecker(),
358
+ };
359
+ }
360
+ synthesisInitialPageLoad(target, intermediates, options = {}) {
361
+ const visitOrder = [];
362
+ // visit the target page/tab
363
+ visitOrder.push(this.getAction(target, 'target'));
364
+ if (intermediates.length > 0) {
365
+ // intermediate steps, final step, and cleanup steps
366
+ if (!options.final) {
367
+ throw core_1.utils.haltOrThrow(`${target}-init-load doesn't have a final step`);
368
+ }
369
+ this.synthesisFinalPart(visitOrder, options.final, intermediates, options);
370
+ }
371
+ return {
372
+ name: options.name || `${target}-init-load`,
373
+ appName: this.getAppName(),
374
+ type: 'init-page-load',
375
+ newTestUser: !!options.newTestUser,
376
+ device: this.getDevice(),
377
+ baseURL: this.getNormalizedBaseURL(options),
378
+ URLParameters: this.getURLParameters(options),
379
+ tabsOrder: visitOrder,
380
+ numOfWarmup: this.getActualNumberOfWarmup(),
381
+ dataBuilder: options.dataBuilder,
382
+ isPageLoaded: this.getPageLoadChecker(),
383
+ };
384
+ }
385
+ getActionFromStep(step, type, snapshot, screenshot) {
386
+ const action = Object.assign({}, step);
387
+ return this.fillInInfo(action, type, snapshot, screenshot);
388
+ }
389
+ getAction(stepName, type, snapshot, screenshot) {
390
+ const step = this.steps.get(stepName);
391
+ if (!step) {
392
+ throw core_1.utils.haltOrThrow(`step ${stepName} doesn't exist`);
393
+ }
394
+ return this.getActionFromStep(step, type, snapshot, screenshot);
395
+ }
396
+ // by default take snapshot for baseline, target, and final step
397
+ shouldTakeSnapshot(option) {
398
+ const type = option.type;
399
+ return type === 'baseline' || type === 'target' || type === 'final';
400
+ }
401
+ // by default every step takes one screenshot
402
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
403
+ shouldTakeScreenshot(_option) {
404
+ return true;
405
+ }
406
+ fillInInfo(actionBasic, type, snapshot, screenshot) {
407
+ const action = Object.assign({}, actionBasic);
408
+ // fill in snapshot and screenshot instructions
409
+ if (typeof snapshot !== 'boolean') {
410
+ snapshot = this.shouldTakeSnapshot({ action, type });
411
+ }
412
+ if (typeof screenshot !== 'boolean') {
413
+ screenshot = this.shouldTakeScreenshot({ action, type });
414
+ }
415
+ action.snapshot = this.config.skipSnapshot ? false : snapshot;
416
+ action.screenshot = skipAllScreenshot ? false : screenshot;
417
+ if (type && type !== 'init') {
418
+ action.type = type;
419
+ }
420
+ const interactions = action.interactions;
421
+ // fill in extra interactions
422
+ if (!Array.isArray(interactions)) {
423
+ action.interactions = [interactions];
424
+ }
425
+ action.interactions = action.interactions.concat(this.getExtraOperationsForStep(action));
426
+ // make sure action.url is normalized
427
+ while (action.url.startsWith('/')) {
428
+ action.url = action.url.substring(1);
429
+ }
430
+ return action;
431
+ }
432
+ }
433
+ exports.default = BaseSynthesizer;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @emails oncall+ws_labs
8
+ * @format
9
+ */
10
+ import type { AnyFunction, OperationArgs, MemLabConfig } from '@memlab/core';
11
+ import type { CDPSession, Page } from 'puppeteer';
12
+ import { TestPlanner } from './lib/operations/TestPlanner';
13
+ declare type PageInteractOptions = {
14
+ config?: MemLabConfig;
15
+ testPlanner?: TestPlanner;
16
+ };
17
+ export default class E2EInteractionManager {
18
+ private cdpsession;
19
+ private page;
20
+ private pageHistoryLength;
21
+ private evalFuncAfterInitLoad;
22
+ constructor(page: Page);
23
+ getCDPSession(): Promise<CDPSession>;
24
+ clearCDPSession(): void;
25
+ setEvalFuncAfterInitLoad(func: AnyFunction | null): void;
26
+ protected initialLoad(page: Page, url: string, opArgs?: OperationArgs): Promise<void>;
27
+ private beforeInteractions;
28
+ visitAndGetSnapshots(options?: PageInteractOptions): Promise<void>;
29
+ warmupInPage(): Promise<void>;
30
+ private visitPage;
31
+ private getPageStatistics;
32
+ private interactWithPage;
33
+ private startTrackingHeap;
34
+ private writeSnapshotFileFromCDPSession;
35
+ private saveHeapSnapshotToFile;
36
+ private fullGC;
37
+ private forceGC;
38
+ private collectMetrics;
39
+ }
40
+ export {};
41
+ //# sourceMappingURL=E2EInteractionManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"E2EInteractionManager.d.ts","sourceRoot":"","sources":["../src/E2EInteractionManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,WAAW,EAIX,aAAa,EAEb,YAAY,EACb,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAC,UAAU,EAAE,IAAI,EAAC,MAAM,WAAW,CAAC;AAOhD,OAA2B,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAC;AAK7E,aAAK,mBAAmB,GAAG;IACzB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,CAAC;AAoBF,MAAM,CAAC,OAAO,OAAO,qBAAqB;IACxC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,iBAAiB,CAAgB;IACzC,OAAO,CAAC,qBAAqB,CAA4B;gBAE7C,IAAI,EAAE,IAAI;IAIT,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAO1C,eAAe,IAAI,IAAI;IAIvB,wBAAwB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI;cAI/C,WAAW,CACzB,IAAI,EAAE,IAAI,EACV,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,aAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;YAmBF,kBAAkB;IASnB,oBAAoB,CAC/B,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,IAAI,CAAC;IA0DH,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YA4C5B,SAAS;YAeT,iBAAiB;YAgEjB,gBAAgB;YAoBhB,iBAAiB;YAQjB,+BAA+B;YAwC/B,sBAAsB;YActB,MAAM;YAeN,OAAO;YAUP,cAAc;CA4B7B"}