diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/point_of_sale/static/tests/tours/helpers/utils.js | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (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.js | 153 |
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 }; +}); |
