Giter VIP home page Giter VIP logo

ember-yadda-backup1's People

Contributors

lolmaus avatar

Watchers

 avatar  avatar  avatar  avatar

ember-yadda-backup1's Issues

[RFC] Initial design

File locations

Path Use Default import must return
/tests/yadda/steps.js Step library to be used for feature files A hash of opinionated steps
/tests/yadda/steps/<my-steps>.js Custom step implementations A hash of opinionated steps, to be composed into main steps.js
/tests/yadda/dictionary.js Converters aka macros An hash of string | Regexp | [string | RegExp, (string[]) => Promise<any> ]
/tests/yadda/labels.js Custom labels for third-party selectors An hash of string, e. g. {'Bootstrap-Primary-Button', '.btn-primary')
/tests/yadda/annotations.js Annotations to setup features and scenarios TBD

Populating the dictionary

Yadda has a weird API for defining the dictionary:

new Yadda.Dictionary()
  .define('address', '$street, $postcode')
  .define('street', /(\d+) (\w+))
  .define('postcode', /((GIR &0AA)|((([A-PR-UWYZ][A-HK-Y]?[0-9][0-9]?)|(([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRV-Y]))) &[0-9][ABD-HJLNP-UW-Z]{2}))/)
  .define('num', /(\d+)/, Yadda.converters.integer)
  .define('quantity', /(\d+) (\w+)/, (amount, units, cb) => {
    cb(null, { amount: amount, units: units })
  });
  .define('user', '$user', (userName, cb) => {
    fetch(`https://api.example.com/users/${userName}`).then(
      (response) => cb(null, response.json()),
      (e) => cb(e)
    );
  });

This Node-style callback is inconvenient and hard to type. Basically, you pass a callback that accepts a callback.

ember-yadda uses a simplified way of defining the dictionary:

{
  address: '$street, $postcode',
  street: /(\d+) (\w+),
  postcode: /((GIR &0AA)|((([A-PR-UWYZ][A-HK-Y]?[0-9][0-9]?)|(([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRV-Y]))) &[0-9][ABD-HJLNP-UW-Z]{2}))/,
  num: Yadda.converters.integer,

  quantity: [
    /(\d+) (\w+)/,
    (amount, units) => ({ amount: amount, units: units })
  ],

  user: [
    '$user',
    async (userName) => (await fetch(`https://api.example.com/users/${userName}`)).json()
  ]
}

Under the hood, ember-yadda will convert these straightforward callbacks into Node-style supported by Yadda like this:

import opinionatedDictionary from '<my-app>/tests/yadda/dictionary';

function instantiateYaddaDictionary(opinionatedDictionary) {
  const yaddaDictionary = new Yadda.Dictionary();

  Object.entries(opinionatedDictionary).forEach(([macroName, macroDefinition]) => {
    if (Array.isArray(macroDefinition)) {
      const [pattern, converter] = macroDefinition;
      yaddaDictionary.define(macroName, pattern, wrapConverterWithNodeStyle(converter));
    } else {
      yaddaDictionary.define(macroName, macroDefinition);
    }
  });

  return yaddaDictionary;
}

function wrapConverterWithNodeStyle(converter) {
  return (...args) => {
    const cb = args.pop();

    try {
      const result = await converter(...args);
      cb(null, result);
    } catch (e) {
      cb(e);
    }
  }
}

export default instantiateYaddaDictionary(opinionatedDictionary);

Now it is very convenient to define converter macros, but existing converters from Yadda.converters are Node-style. I see three ways to resolve this:

  1. When defining a macro using a Yadda.converters converter, use an util on it so that it is wrapped with async/await that the addon supports.

    E. g. instead of:

    num: Yadda.converters.integer,

    you would do:

    num: converter(Yadda.converters.integer),

    The downside of this approach is that a Node-style converter will be wrapped with async/await by the util and then wrapped again with Node-style by the addon.

  2. Have wrapConverterWithNodeStyle detect whether the converter is Node-style or async/await.

    One way to do this would be to use introspection:

    import introspect from 'introspect-fun'; // https://github.com/NicolasVargas/introspect-fun
    
    function maybeWrapConverterWithNodeStyle(converter) {
      return introspect(coverter).pop() === 'next'
        ? converter
        : wrapConverterWithNodeStyle(converter);
    }
    

    The downside of this approach is that it's magical and unreliable, since it assumes that a node-style callback has its last argument called next. It will fail both when a Yadda.converters converter has the last argument called differently (currently not the case) and when an async/await converter has its last argument called next.

    There's also a performance implication, but it should be negligible.

    Maybe there's another way to distinguish Node-style Yadda.converters converters from async/await converters?

  3. Simply port all six Yadda.converters converters, so that either the ember-yadda addon or a separate content-oriented addon offers async/await equivalents.

    Downside: more work to do. I was gonna write "more maintenance", but after ensuring test coverage the code will not need to be revisited.

    We'll also need to maintain compatibility with Yadda.converters, but I doubt that they will stray far from what they currently are.

Supporting legacy ember-cli-yadda

In the legacy ember-cli-yadda, a test file generated from a feature file imports a matching steps file like this.

We could make this import conditional. If the steps file exists, run it using the legacy setup. Otherwise, use the new setup described above.

Within the steps file, the user has complete freedom. They can:

  1. Keep using the new setup, but override some of the opinionated steps. This could be useful to resolve step name conflicts between opinionated step libraries.
  2. Use the legacy approach just like in ember-cli-yadda.
  3. Use a mixed approach via tricks from ember-cli-yadda-opinionated. This lets you use legacy ember-cli-yadda steps and opinionated steps in the same feature.

Things not covered

Localization

Previously, localizaton setup was happening in the app.

Due to drastically simplified design of addon's in-app files, the addon will have yadda.localisation.default hardcoded.

I believe internationalized steps are a bad practice, so we should not bother making it configurable.

In future, if there's a feature request for supporting non-English locales, we'll be able to think about a way of making it configurable.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.