@mablhq/mabl-cli 1.58.20 → 1.58.25

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 (108) hide show
  1. package/api/basicApiClient.js +21 -15
  2. package/api/mablApiClient.js +40 -48
  3. package/api/mablApiClientFactory.js +1 -1
  4. package/auth/OktaClient.js +1 -2
  5. package/browserEngines/chromiumBrowserEngine.js +1 -2
  6. package/browserEngines/firefoxBrowserEngine.js +2 -2
  7. package/browserEngines/webkitBrowerEngine.js +2 -2
  8. package/browserLauncher/browserLauncherFactory.js +2 -3
  9. package/browserLauncher/elementHandle.js +1 -2
  10. package/browserLauncher/playwrightBrowserLauncher/chromium/chromiumElementHandleDelegate.js +12 -20
  11. package/browserLauncher/playwrightBrowserLauncher/chromium/chromiumFrameDelegate.js +1 -1
  12. package/browserLauncher/playwrightBrowserLauncher/chromium/chromiumPageDelegate.js +7 -10
  13. package/browserLauncher/playwrightBrowserLauncher/firefox/firefoxFrameDelegate.js +3 -4
  14. package/browserLauncher/playwrightBrowserLauncher/nonChromium/nonChromiumAbstractElementHandleDelegate.js +4 -5
  15. package/browserLauncher/playwrightBrowserLauncher/nonChromium/nonChromiumAbstractPageDelegate.js +4 -7
  16. package/browserLauncher/playwrightBrowserLauncher/playwrightBrowser.js +1 -1
  17. package/browserLauncher/playwrightBrowserLauncher/playwrightBrowserLauncher.js +6 -5
  18. package/browserLauncher/playwrightBrowserLauncher/playwrightDom.js +31 -37
  19. package/browserLauncher/playwrightBrowserLauncher/playwrightFrame.js +8 -9
  20. package/browserLauncher/playwrightBrowserLauncher/playwrightHttpRequest.js +4 -8
  21. package/browserLauncher/playwrightBrowserLauncher/playwrightPage.js +4 -6
  22. package/browserLauncher/playwrightBrowserLauncher/webkit/webkitElementHandleDelegate.js +1 -2
  23. package/browserLauncher/playwrightBrowserLauncher/webkit/webkitFrameDelegate.js +1 -1
  24. package/browserTestMonitoring/metricsRecorder.js +6 -6
  25. package/commands/commandUtil/branches.js +2 -3
  26. package/commands/commandUtil/codeInsights.js +13 -16
  27. package/commands/commandUtil/fileUtil.js +1 -1
  28. package/commands/commandUtil/util.js +3 -4
  29. package/commands/commandUtil/versionUtil.js +4 -5
  30. package/commands/config/config_cmds/list.js +2 -2
  31. package/commands/constants.js +3 -1
  32. package/commands/datatables/datatables_cmds/create.js +2 -3
  33. package/commands/datatables/datatables_cmds/export.js +3 -5
  34. package/commands/datatables/datatables_cmds/list.js +1 -2
  35. package/commands/datatables/datatables_cmds/scenarios.js +1 -2
  36. package/commands/datatables/datatables_cmds/update.js +11 -11
  37. package/commands/datatables/utils.js +9 -9
  38. package/commands/deploy/deploy_cmds/awaitDeploymentCompletion.js +6 -8
  39. package/commands/deploy/deploy_cmds/create.js +11 -13
  40. package/commands/deploy/deploy_cmds/executionResultPresenter.js +8 -11
  41. package/commands/deploy/deploy_cmds/list.js +3 -4
  42. package/commands/deploy/deploy_cmds/watch.js +1 -2
  43. package/commands/environments/environments_cmds/create.js +1 -2
  44. package/commands/environments/environments_cmds/delete.js +1 -2
  45. package/commands/environments/environments_cmds/update.js +1 -2
  46. package/commands/environments/environments_cmds/urls_cmds/list.js +1 -2
  47. package/commands/flows/flows_cmds/export.js +1 -2
  48. package/commands/plans/plans_cmds/describe.js +1 -2
  49. package/commands/tests/mobileEmulationUtil.js +5 -7
  50. package/commands/tests/testsUtil.js +40 -42
  51. package/commands/tests/tests_cmds/export.js +1 -2
  52. package/commands/tests/tests_cmds/import.js +4 -5
  53. package/commands/tests/tests_cmds/run-cloud.js +12 -13
  54. package/commands/tests/tests_cmds/run.js +30 -11
  55. package/commands/users/users_cmds/list.js +2 -2
  56. package/commands/workspaces/workspace_cmds/copy.js +1 -2
  57. package/core/execution/ApiTestUtils.js +82 -94
  58. package/core/execution/LocalizationOptionsLists.js +1253 -0
  59. package/core/messaging/logLineMessaging.js +2 -3
  60. package/core/messaging/messaging.js +6 -7
  61. package/core/trainer/trainingSessions.js +15 -15
  62. package/coreWebVitals/index.js +14 -18
  63. package/domUtil/index.js +1 -1
  64. package/execution/index.js +1 -1
  65. package/functions/apiTest/utils.js +4 -5
  66. package/http/MablHttpAgent.js +4 -6
  67. package/http/RequestSecurityError.js +1 -1
  68. package/http/axiosProxyConfig.js +3 -5
  69. package/http/httpUtil.js +2 -3
  70. package/http/requestInterceptor.js +11 -15
  71. package/mablApi/index.js +1 -1
  72. package/mablscript/MablAction.js +2 -3
  73. package/mablscript/MablStep.js +2 -4
  74. package/mablscript/MablSymbol.js +1 -1
  75. package/mablscript/actions/ConditionAction.js +2 -4
  76. package/mablscript/actions/FindAction.js +2 -4
  77. package/mablscript/diffing/diffingUtil.js +8 -7
  78. package/mablscript/importer.js +1 -2
  79. package/mablscript/steps/AccessibilityCheck.js +7 -9
  80. package/mablscript/steps/AssertStep.js +11 -16
  81. package/mablscript/steps/ClickAndHoldStep.js +1 -2
  82. package/mablscript/steps/CookieUtils.js +40 -7
  83. package/mablscript/steps/CreateVariableStep.js +1 -2
  84. package/mablscript/steps/DownloadStep.js +1 -2
  85. package/mablscript/steps/EnterTextStep.js +1 -2
  86. package/mablscript/steps/EvaluateFlowStep.js +1 -1
  87. package/mablscript/steps/IfConditionStep.js +4 -6
  88. package/mablscript/steps/ReleaseStep.js +1 -2
  89. package/mablscript/steps/SendHttpRequestStep.js +2 -4
  90. package/mablscript/steps/SendKeyStep.js +1 -1
  91. package/mablscript/steps/SetCookieStep.js +35 -20
  92. package/mablscript/steps/SetViewportStep.js +1 -1
  93. package/mablscript/steps/SwitchContextStep.js +3 -6
  94. package/mablscript/steps/SyntheticStep.js +1 -2
  95. package/mablscriptFind/index.js +1 -1
  96. package/middleware.js +1 -2
  97. package/package.json +2 -2
  98. package/popupDismissal/index.js +4 -5
  99. package/providers/authenticationProvider.js +5 -6
  100. package/providers/cliConfigProvider.js +10 -12
  101. package/providers/exportRequestProvider.js +3 -5
  102. package/providers/logging/loggingProvider.js +1 -1
  103. package/reporters/mochAwesome/mochAwesomeReporter.js +12 -14
  104. package/resources/mablFind.js +1 -1
  105. package/util/actionabilityUtil.js +1 -1
  106. package/util/analytics.js +6 -9
  107. package/util/browserTestUtils.js +1 -2
  108. package/util/markdownUtil.js +9 -11
package/middleware.js CHANGED
@@ -2,7 +2,6 @@
2
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
- var _a;
6
5
  Object.defineProperty(exports, "__esModule", { value: true });
7
6
  exports.CLI_MIDDLEWARE = void 0;
8
7
  const chalk_1 = __importDefault(require("chalk"));
@@ -15,7 +14,7 @@ const pureUtil_1 = require("./util/pureUtil");
15
14
  const simple_update_notifier_1 = __importDefault(require("simple-update-notifier"));
16
15
  const cliPackage = require('./package.json');
17
16
  const UPDATE_CHECK_INTERVAL_MILLISECONDS = 24 * 60 * 60 * 1000;
18
- const MIN_SUPPORTED_NODE_VERSION = (0, versionUtil_1.extractNodeVersionTuple)((_a = cliPackage === null || cliPackage === void 0 ? void 0 : cliPackage.engines) === null || _a === void 0 ? void 0 : _a.node);
17
+ const MIN_SUPPORTED_NODE_VERSION = (0, versionUtil_1.extractNodeVersionTuple)(cliPackage?.engines?.node);
19
18
  exports.CLI_MIDDLEWARE = [
20
19
  () => {
21
20
  (0, logLineMessaging_1.registerConsoleLoggerForOutput)(messaging_1.mablEventEmitter);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mablhq/mabl-cli",
3
- "version": "1.58.20",
3
+ "version": "1.58.25",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "The official mabl command line interface tool",
6
6
  "main": "index.js",
@@ -30,7 +30,7 @@
30
30
  "@types/fs-extra": "8.1.1",
31
31
  "@types/serve-handler": "6.1.0",
32
32
  "@types/tmp": "0.2.0",
33
- "@types/tough-cookie": "4.0.0",
33
+ "@types/tough-cookie": "4.0.2",
34
34
  "agent-base": "6.0.2",
35
35
  "analytics-node": "3.5.0",
36
36
  "async-mutex": "0.3.1",
@@ -44,7 +44,7 @@ function getEffectiveZIndex(element) {
44
44
  if (zIndex && zIndex !== ZINDEX_AUTO) {
45
45
  return parseInt(zIndex, 10);
46
46
  }
47
- current = (current === null || current === void 0 ? void 0 : current.parentElement) ? current.parentElement : undefined;
47
+ current = current?.parentElement ? current.parentElement : undefined;
48
48
  }
49
49
  return 0;
50
50
  }
@@ -91,9 +91,8 @@ function developCloseCandidates(candidates) {
91
91
  const domCovering = [];
92
92
  const actionableElements = [];
93
93
  candidate.elements.forEach((element) => {
94
- var _a;
95
- if (element === null || element === void 0 ? void 0 : element.offsetParent) {
96
- if (['BUTTON', 'A', 'DIV'].includes((_a = element.tagName) === null || _a === void 0 ? void 0 : _a.toUpperCase()) &&
94
+ if (element?.offsetParent) {
95
+ if (['BUTTON', 'A', 'DIV'].includes(element.tagName?.toUpperCase()) &&
97
96
  checkElementAttributes(element) &&
98
97
  !actionableElements.includes(element)) {
99
98
  actionableElements.push(element);
@@ -171,7 +170,7 @@ function getAllElementsAbove(targetElement, elements) {
171
170
  return [];
172
171
  }
173
172
  const targetZindex = getEffectiveZIndex(targetElement);
174
- const zIndexAboveTarget = Math.min(...elements === null || elements === void 0 ? void 0 : elements.map((el) => getEffectiveZIndex(el)));
173
+ const zIndexAboveTarget = Math.min(...elements?.map((el) => getEffectiveZIndex(el)));
175
174
  if (zIndexAboveTarget <= targetZindex) {
176
175
  return [];
177
176
  }
@@ -106,7 +106,6 @@ class AuthenticationProvider {
106
106
  return config.authentication.authType;
107
107
  }
108
108
  async displayInfo() {
109
- var _a;
110
109
  try {
111
110
  const config = await cliConfigProvider_1.CliConfigProvider.getCliConfig();
112
111
  if (config.authentication.authType === types_1.AuthType.ApiKey) {
@@ -118,7 +117,8 @@ class AuthenticationProvider {
118
117
  return;
119
118
  }
120
119
  loggingProvider_1.logger.info(`Logged in as user [${chalk.magenta.bold(config.email)}]`);
121
- const expirationMilliseconds = ((_a = config.authentication.refreshTokenExpirationTimeMilliseconds) !== null && _a !== void 0 ? _a : config.authentication.accessTokenExpirationTimeMilliseconds) -
120
+ const expirationMilliseconds = (config.authentication.refreshTokenExpirationTimeMilliseconds ??
121
+ config.authentication.accessTokenExpirationTimeMilliseconds) -
122
122
  Date.now();
123
123
  if (expirationMilliseconds > 0) {
124
124
  loggingProvider_1.logger.info(`Login expires in [${chalk.magenta.bold(humanizeExpirationTime(expirationMilliseconds))}]`);
@@ -218,13 +218,12 @@ ${authUrl}
218
218
  }
219
219
  exports.AuthenticationProvider = AuthenticationProvider;
220
220
  function processAuthorizationError(error, isTokenRefresh) {
221
- var _a;
222
221
  const mainErrorMessage = isTokenRefresh ? 'Token refresh' : 'Authorization';
223
222
  loggingProvider_1.logger.error(chalk.red.bold(`${mainErrorMessage} failed.`));
224
223
  loggingProvider_1.logger.error(chalk.red.bold(error.toString()));
225
224
  let hasResponseData = false;
226
225
  if (axios_1.default.isAxiosError(error)) {
227
- const data = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
226
+ const data = error.response?.data;
228
227
  if (isOidcError(data)) {
229
228
  loggingProvider_1.logger.error(chalk.red.bold(`${mainErrorMessage} failed: ${data.error_description} [${data.error}]`));
230
229
  hasResponseData = true;
@@ -248,7 +247,7 @@ function humanizeExpirationTime(milliseconds) {
248
247
  });
249
248
  }
250
249
  function isOidcError(err) {
251
- return ((0, pureUtil_1.isString)(err === null || err === void 0 ? void 0 : err.error) &&
252
- (0, pureUtil_1.isString)(err === null || err === void 0 ? void 0 : err.error_description));
250
+ return ((0, pureUtil_1.isString)(err?.error) &&
251
+ (0, pureUtil_1.isString)(err?.error_description));
253
252
  }
254
253
  exports.isOidcError = isOidcError;
@@ -60,7 +60,6 @@ exports.AUTH_KEY_NAMES = [
60
60
  ];
61
61
  let isKeytarAvailable = true;
62
62
  async function getEncryptionKey() {
63
- var _a;
64
63
  if ((0, utilities_1.areWeTestingInJest)()) {
65
64
  return;
66
65
  }
@@ -69,7 +68,7 @@ async function getEncryptionKey() {
69
68
  try {
70
69
  const keytar = require('keytar');
71
70
  maybeEncryptionKey =
72
- (_a = (await keytar.getPassword(env_1.CONF_FILE_PROJECT_NAME, env_1.ENV))) !== null && _a !== void 0 ? _a : undefined;
71
+ (await keytar.getPassword(env_1.CONF_FILE_PROJECT_NAME, env_1.ENV)) ?? undefined;
73
72
  if (!maybeEncryptionKey) {
74
73
  maybeEncryptionKey = crypto
75
74
  .randomFillSync(Buffer.alloc(48))
@@ -212,27 +211,26 @@ class CliConfigProvider {
212
211
  return this.getCliConfig();
213
212
  }
214
213
  static async setCliConfig(config) {
215
- var _a, _b, _c, _d, _e, _f;
216
- await setValue(AUTH_PROVIDER, (_a = config === null || config === void 0 ? void 0 : config.authentication) === null || _a === void 0 ? void 0 : _a.authProvider);
217
- await setValue(AUTH_TYPE, (_b = config === null || config === void 0 ? void 0 : config.authentication) === null || _b === void 0 ? void 0 : _b.authType);
218
- await setValue(ACCESS_TOKEN_NAME, (_c = config === null || config === void 0 ? void 0 : config.authentication) === null || _c === void 0 ? void 0 : _c.accessToken);
219
- await setValue(REFRESH_TOKEN_NAME, (_d = config === null || config === void 0 ? void 0 : config.authentication) === null || _d === void 0 ? void 0 : _d.refreshToken);
220
- await setValue(ACCESS_TOKEN_EXPIRATION_MILLISECONDS_NAME, (_e = config === null || config === void 0 ? void 0 : config.authentication) === null || _e === void 0 ? void 0 : _e.accessTokenExpirationTimeMilliseconds);
221
- await setValue(REFRESH_TOKEN_EXPIRATION_MILLISECONDS_NAME, (_f = config === null || config === void 0 ? void 0 : config.authentication) === null || _f === void 0 ? void 0 : _f.refreshTokenExpirationTimeMilliseconds);
214
+ await setValue(AUTH_PROVIDER, config?.authentication?.authProvider);
215
+ await setValue(AUTH_TYPE, config?.authentication?.authType);
216
+ await setValue(ACCESS_TOKEN_NAME, config?.authentication?.accessToken);
217
+ await setValue(REFRESH_TOKEN_NAME, config?.authentication?.refreshToken);
218
+ await setValue(ACCESS_TOKEN_EXPIRATION_MILLISECONDS_NAME, config?.authentication?.accessTokenExpirationTimeMilliseconds);
219
+ await setValue(REFRESH_TOKEN_EXPIRATION_MILLISECONDS_NAME, config?.authentication?.refreshTokenExpirationTimeMilliseconds);
222
220
  await setValue(USER_ID_NAME, config.userId);
223
221
  await setValue(EMAIL_NAME, config.email);
224
222
  await setValue(USER_FULL_NAME_NAME, config.userFullName);
225
223
  return config;
226
224
  }
227
225
  static async setCliAuthInfo(authInfo, updateMilliseconds) {
228
- await setValue(AUTH_TYPE, authInfo === null || authInfo === void 0 ? void 0 : authInfo.auth_type);
226
+ await setValue(AUTH_TYPE, authInfo?.auth_type);
229
227
  switch (authInfo.auth_type) {
230
228
  case types_2.AuthType.Bearer:
231
229
  const bearerAuthInfo = authInfo;
232
230
  const expirationTimeMilliseconds = updateMilliseconds + bearerAuthInfo.expires_in * 1000;
233
231
  await setValue(AUTH_PROVIDER, bearerAuthInfo.auth_provider);
234
- await setValue(ACCESS_TOKEN_NAME, bearerAuthInfo === null || bearerAuthInfo === void 0 ? void 0 : bearerAuthInfo.access_token);
235
- await setValue(REFRESH_TOKEN_NAME, bearerAuthInfo === null || bearerAuthInfo === void 0 ? void 0 : bearerAuthInfo.refresh_token);
232
+ await setValue(ACCESS_TOKEN_NAME, bearerAuthInfo?.access_token);
233
+ await setValue(REFRESH_TOKEN_NAME, bearerAuthInfo?.refresh_token);
236
234
  await setValue(ACCESS_TOKEN_EXPIRATION_MILLISECONDS_NAME, expirationTimeMilliseconds);
237
235
  if (bearerAuthInfo.refresh_token_expires_at) {
238
236
  await setValue(REFRESH_TOKEN_EXPIRATION_MILLISECONDS_NAME, bearerAuthInfo.refresh_token_expires_at * 1000);
@@ -23,8 +23,7 @@ class ExportRequestProvider {
23
23
  this.outputFilepath = outputFilepath;
24
24
  }
25
25
  isExportIncomplete() {
26
- var _a;
27
- if ((_a = this.latestRequest) === null || _a === void 0 ? void 0 : _a.status) {
26
+ if (this.latestRequest?.status) {
28
27
  return this.continuationStatuses.includes(this.latestRequest.status);
29
28
  }
30
29
  return false;
@@ -58,8 +57,7 @@ class ExportRequestProvider {
58
57
  return this.latestRequest;
59
58
  }
60
59
  async awaitCompletion() {
61
- var _a;
62
- if (!((_a = this.latestRequest) === null || _a === void 0 ? void 0 : _a.id)) {
60
+ if (!this.latestRequest?.id) {
63
61
  throw new Error('CreateExportRequest is undefined');
64
62
  }
65
63
  await this.pollForStatusUpdate(this.latestRequest.id);
@@ -153,7 +151,7 @@ class ExportRequestProvider {
153
151
  this.exportDownloadSpinner,
154
152
  ]
155
153
  .forEach((spinner) => {
156
- if (spinner === null || spinner === void 0 ? void 0 : spinner.isSpinning) {
154
+ if (spinner?.isSpinning) {
157
155
  spinner.fail();
158
156
  }
159
157
  });
@@ -68,7 +68,7 @@ exports.logger = winston.createLogger({
68
68
  defaultMeta: { service: 'mabl-cli' },
69
69
  transports: [
70
70
  new CliTransport({
71
- format: winston.format.printf((info) => { var _a; return `${(_a = info.message) !== null && _a !== void 0 ? _a : info[MESSAGE]}`; }),
71
+ format: winston.format.printf((info) => `${info.message ?? info[MESSAGE]}`),
72
72
  }),
73
73
  ],
74
74
  });
@@ -102,7 +102,7 @@ function generatePlainSuiteFromTestResults(testResults) {
102
102
  const command = constructTestRunCommand();
103
103
  return {
104
104
  uuid: parentId,
105
- title: command !== null && command !== void 0 ? command : `mabl CLI tests`,
105
+ title: command ?? `mabl CLI tests`,
106
106
  fullFile: '',
107
107
  file: '',
108
108
  beforeHooks: [],
@@ -121,9 +121,8 @@ function generatePlainSuiteFromTestResults(testResults) {
121
121
  }
122
122
  exports.generatePlainSuiteFromTestResults = generatePlainSuiteFromTestResults;
123
123
  function generatePlainTestForTestRun(parentUuid, testResult) {
124
- var _a;
125
124
  return {
126
- title: (_a = testResult.testName) !== null && _a !== void 0 ? _a : '',
125
+ title: testResult.testName ?? '',
127
126
  fullTitle: `${testResult.testName} - ${testResult.testId}`,
128
127
  timedOut: false,
129
128
  code: getCodeForResult(testResult),
@@ -200,7 +199,6 @@ function generateHoldingTestSuiteObj(suite) {
200
199
  };
201
200
  }
202
201
  function generateReportOptions(supplied) {
203
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
204
202
  if (supplied.json) {
205
203
  supplied.saveJson = supplied.json;
206
204
  supplied.saveHtml = !supplied.saveJson;
@@ -210,16 +208,16 @@ function generateReportOptions(supplied) {
210
208
  supplied.saveJson = !supplied.saveHtml;
211
209
  }
212
210
  return {
213
- reportFilename: (_a = supplied.reportFilename) !== null && _a !== void 0 ? _a : `mabl-mochawesomeReport`,
214
- reportDir: (_b = supplied.reportDir) !== null && _b !== void 0 ? _b : path.normalize(`./mablReports`),
215
- overwrite: (_c = supplied.overwrite) !== null && _c !== void 0 ? _c : true,
216
- reportTitle: (_d = supplied.reportTitle) !== null && _d !== void 0 ? _d : `mabl mochawesome report`,
217
- reportPageTitle: (_e = supplied.reportPageTitle) !== null && _e !== void 0 ? _e : `mabl mochawesome report`,
218
- autoOpen: (_f = supplied.autoOpen) !== null && _f !== void 0 ? _f : !supplied.saveJson,
219
- showPassed: (_g = supplied.showPassed) !== null && _g !== void 0 ? _g : true,
220
- showFailed: (_h = supplied.showFailed) !== null && _h !== void 0 ? _h : true,
221
- showPending: (_j = supplied.showPending) !== null && _j !== void 0 ? _j : true,
222
- showSkipped: (_k = supplied.showSkipped) !== null && _k !== void 0 ? _k : true,
211
+ reportFilename: supplied.reportFilename ?? `mabl-mochawesomeReport`,
212
+ reportDir: supplied.reportDir ?? path.normalize(`./mablReports`),
213
+ overwrite: supplied.overwrite ?? true,
214
+ reportTitle: supplied.reportTitle ?? `mabl mochawesome report`,
215
+ reportPageTitle: supplied.reportPageTitle ?? `mabl mochawesome report`,
216
+ autoOpen: supplied.autoOpen ?? !supplied.saveJson,
217
+ showPassed: supplied.showPassed ?? true,
218
+ showFailed: supplied.showFailed ?? true,
219
+ showPending: supplied.showPending ?? true,
220
+ showSkipped: supplied.showSkipped ?? true,
223
221
  saveJson: supplied.saveJson,
224
222
  saveHtml: supplied.saveHtml,
225
223
  };