summaryrefslogtreecommitdiff
path: root/addons/point_of_sale/static/tests/tours/helpers/utils.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/point_of_sale/static/tests/tours/helpers/utils.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/point_of_sale/static/tests/tours/helpers/utils.js')
-rw-r--r--addons/point_of_sale/static/tests/tours/helpers/utils.js153
1 files changed, 153 insertions, 0 deletions
diff --git a/addons/point_of_sale/static/tests/tours/helpers/utils.js b/addons/point_of_sale/static/tests/tours/helpers/utils.js
new file mode 100644
index 00000000..e8fcc591
--- /dev/null
+++ b/addons/point_of_sale/static/tests/tours/helpers/utils.js
@@ -0,0 +1,153 @@
+odoo.define('point_of_sale.tour.utils', function (require) {
+ 'use strict';
+
+ const config = require('web.config');
+
+ /**
+ * USAGE
+ * -----
+ *
+ * ```
+ * const { startSteps, getSteps, createTourMethods } = require('point_of_sale.utils');
+ * const { Other } = require('point_of_sale.tour.OtherMethods');
+ *
+ * // 1. Define classes Do, Check and Execute having methods that
+ * // each return array of tour steps.
+ * class Do {
+ * click() {
+ * return [{ content: 'click button', trigger: '.button' }];
+ * }
+ * }
+ * class Check {
+ * isHighligted() {
+ * return [{ content: 'button is highlighted', trigger: '.button.highlight', run: () => {} }];
+ * }
+ * }
+ * // Notice that Execute has access to methods defined in Do and Check classes
+ * // Also, we can compose steps from other module.
+ * class Execute {
+ * complexSteps() {
+ * return [...this._do.click(), ...this._check.isHighlighted(), ...Other._exec.complicatedSteps()];
+ * }
+ * }
+ *
+ * // 2. Instantiate these class definitions using `createTourMethods`.
+ * // The returned object gives access to the defined methods above
+ * // thru the do, check and exec properties.
+ * // - do gives access to the methods defined in Do class
+ * // - check gives access to the methods defined in Check class
+ * // - exec gives access to the methods defined in Execute class
+ * const Screen = createTourMethods('Screen', Do, Check, Execute);
+ *
+ * // 3. Call `startSteps` to start empty steps.
+ * startSteps();
+ *
+ * // 4. Call the tour methods to populate the steps created by `startSteps`.
+ * Screen.do.click(); // return of this method call is added to steps created by startSteps
+ * Screen.check.isHighlighted() // same as above
+ * Screen.exec.complexSteps() // same as above
+ *
+ * // 5. Call `getSteps` which returns the generated tour steps.
+ * const steps = getSteps();
+ * ```
+ */
+ let steps = [];
+
+ function startSteps() {
+ // always start by waiting for loading to finish
+ steps = [
+ {
+ content: 'wait for loading to finish',
+ trigger: 'body:not(:has(.loader))',
+ run: function () {},
+ },
+ ];
+ }
+
+ function getSteps() {
+ return steps;
+ }
+
+ // this is the method decorator
+ // when the method is called, the generated steps are added
+ // to steps
+ const methodProxyHandler = {
+ apply(target, thisArg, args) {
+ const res = target.call(thisArg, ...args);
+ if (config.isDebug()) {
+ // This step is added before the real steps.
+ // Very useful when debugging because we know which
+ // method call failed and what were the parameters.
+ const constructor = thisArg.constructor.name.split(' ')[1];
+ const methodName = target.name.split(' ')[1];
+ const argList = args
+ .map((a) => (typeof a === 'string' ? `'${a}'` : `${a}`))
+ .join(', ');
+ steps.push({
+ content: `DOING "${constructor}.${methodName}(${argList})"`,
+ trigger: '.pos',
+ run: () => {},
+ });
+ }
+ steps.push(...res);
+ return res;
+ },
+ };
+
+ // we proxy get of the method to decorate the method call
+ const proxyHandler = {
+ get(target, key) {
+ const method = target[key];
+ if (!method) {
+ throw new Error(`Tour method '${key}' is not available.`);
+ }
+ return new Proxy(method.bind(target), methodProxyHandler);
+ },
+ };
+
+ /**
+ * Creates an object with `do`, `check` and `exec` properties which are instances of
+ * the given `Do`, `Check` and `Execute` classes, respectively. Calling methods
+ * automatically adds the returned steps to the steps created by `startSteps`.
+ *
+ * There are however underscored version (_do, _check, _exec).
+ * Calling methods thru the underscored version does not automatically
+ * add the returned steps to the current steps array. Useful when composing
+ * steps from other methods.
+ *
+ * @param {String} name
+ * @param {Function} Do class containing methods which return array of tour steps
+ * @param {Function} Check similar to Do class but the steps are mainly for checking
+ * @param {Function} Execute class containing methods which return array of tour steps
+ * but has access to methods of Do and Check classes via .do and .check,
+ * respectively. Here, we define methods that return tour steps based
+ * on the combination of steps from Do and Check.
+ */
+ function createTourMethods(name, Do, Check = class {}, Execute = class {}) {
+ Object.defineProperty(Do, 'name', { value: `${name}.do` });
+ Object.defineProperty(Check, 'name', { value: `${name}.check` });
+ Object.defineProperty(Execute, 'name', {
+ value: `${name}.exec`,
+ });
+ const methods = { do: new Do(), check: new Check(), exec: new Execute() };
+ // Allow Execute to have access to methods defined in Do and Check
+ // via do and exec, respectively.
+ methods.exec._do = methods.do;
+ methods.exec._check = methods.check;
+ return {
+ Do,
+ Check,
+ Execute,
+ [name]: {
+ do: new Proxy(methods.do, proxyHandler),
+ check: new Proxy(methods.check, proxyHandler),
+ exec: new Proxy(methods.exec, proxyHandler),
+ _do: methods.do,
+ _check: methods.check,
+ _exec: methods.exec,
+ },
+ };
+ }
+
+ return { startSteps, getSteps, createTourMethods };
+});