Cucumber-js: Demande de fonctionnalité : interface utilisateur qui n'utilise pas le mot-clé « this »

Créé le 31 janv. 2017  ·  16Commentaires  ·  Source: cucumber/cucumber-js

Étant donné un monde tel que :

require('cucumber').defineSupportCode( ({setWorldConstructor:world}) => {
    world(class { 
      constructor(p) { ... }
      foo(bar) { return new Promise(.... ) }
    })
})

Je veux pouvoir écrire :

  When(/^I do the foo with (.*)$/i, (ctx, bar) => ctx.foo(bar) );

à la place de

  When(/^I do the foo with (.*)$/i, function(bar) {
      return this.foo(bar)
  });

Cela me débarrassera également de l'utilisation de .bind(this) sur toutes les opérations asynchrones, ou si vous préférez, de l'utilisation de var that = this .

Un autre avantage (IMHO) - cela aidera les gens à sortir de l'arbre d'héritage pour les mondes et à mieux les orienter vers les meilleurs pouvoirs de JS

accepted enhancement

Commentaire le plus utile

D'accord. Comme tout le monde n'est pas un programmeur fonctionnel et pour éviter l'introduction d'un changement radical, que pensez-vous de faire une option qui fait passer le monde comme premier paramètre ?

defineSupportCode(({setWorldInjectionStrategy}) => {
  setWorldInjectionStrategy('this') // default
  setWorldInjectionStrategy('argument') // makes world be the first argument to steps and hooks 
})

Tous les 16 commentaires

Je suis d'accord que cela signifie que vous acceptez toujours world comme argument, et c'est un changement d'API.
Je pense que comme le moka qui a --ui exports et --ui tdd et --ui bdd - la cucue peut aussi.
Il s'agit essentiellement d'une variable de projet qui détermine si les étapes sont appelées/appliquées sur le monde, ou si elles l'acceptent comme premier argument.

Je ne pense vraiment pas que cela en vaudrait la peine sur le long terme. Oui, c'est bien pour ces étapes d'une ligne où vous pouvez utiliser le => là où vous ne pourriez pas le faire autrement. D'après mon expérience, j'ai eu très peu d'étapes comme celle-ci. Je suis surpris que ES6 n'inclue pas de fonction de flèche qui ne conserve pas le contexte car ce serait l'idéal pour moi.

Cela me débarrassera également de l'utilisation de .bind(this) sur toutes les opérations asynchrones, ou si vous préférez - de l'utilisation de var that = this .

Ne pouvez-vous pas utiliser la grosse flèche à l'intérieur des définitions d'étape afin d'éviter cela ?

Un autre avantage (IMHO) - cela aidera les gens à sortir de l'arbre d'héritage pour les mondes et à mieux les orienter vers les meilleurs pouvoirs de JS

Pouvez-vous expliquer cela un peu plus / donner un exemple

En termes simples : j'appartiens à une école de programmeurs qui ont évité la manière de formuler un contexte en tant que classe. Plus les gens sont exposés au côté obscur de la POO, plus cette école grandit - et le mouvement dans la communauté JS qui a empêché la fonction flèche de conserver le contexte est un exemple de la force de ce mouvement.

Sans entrer dans un débat religieux - je dirai juste que je crois en vivre et laisser vivre, tu veux utiliser les cours ? super. S'il te

Mon sentiment général est que cela pourrait être un petit changement, et je serai heureux de le faire si vous pouvez me fournir l'orientation générale de la meilleure façon de procéder ;-)

Lorsque nous définissons des étapes, nous définissons des fonctions, pas des classes, donc logiquement this n'y a pas sa place.

Je pense que c'est une situation pour le paradigme PF plutôt que le paradigme OOP.

@osher Je pense que nous pouvons donner à @charlierudolph le bénéfice du doute qu'il ne veut pas vous forcer à utiliser des cours. Cela dit, il ne devrait pas être obligé d'intégrer (rupture) des modifications d'API partout où il utilise ce package, à moins qu'il n'offre un avantage tangible.

@charlierudolph Je pense que par "les meilleurs pouvoirs de JS" @osher signifie FP, programmation fonctionnelle.

À mon humble avis, ce changement rendrait les définitions des étapes un peu plus simples et plus logiques. Cela supprimerait simplement cette ligne : const thisWorldNotThisStep = this

Il n'est certainement pas nécessaire d'utiliser .bind(this) pour toutes vos fonctions asynchrones, et tant que vous utilisez des fonctions fléchées dans la définition de pas, vous n'avez pas besoin d'utiliser le modèle const self = this .

Le problème (ce qui n'est pas un gros problème) est qu'à l'intérieur d'une définition d'étape, this fait référence au monde, pas à l'étape, ce qui est contre-intuitif.

D'accord. Comme tout le monde n'est pas un programmeur fonctionnel et pour éviter l'introduction d'un changement radical, que pensez-vous de faire une option qui fait passer le monde comme premier paramètre ?

defineSupportCode(({setWorldInjectionStrategy}) => {
  setWorldInjectionStrategy('this') // default
  setWorldInjectionStrategy('argument') // makes world be the first argument to steps and hooks 
})

Je suis tout à fait d'accord avec l'idée d'injecter le monde/contexte a un paramètre du pas. A partir de ce projet, tout l'écosystème JS bouge pour adopter pleinement les nouvelles fonctionnalités permises par ES6 et c'est une bonne chose ❤️

Si vous regardez sur d'autres outils comme Express ou Koa, ils définissent le contexte de la requête actuelle comme this ET comme premier paramètre du middleware (c'est l'équivalent du pas de concombre). Cette solution permet l'utilisation traditionnelle de la fonction et l'utilisation de la fonction flèche ES6.
Un autre avantage du contexte en tant que premier paramètre est que

Pour la solution proposée par @charlierudolph , je ne pense pas que cela puisse fonctionner sur le long terme : cela divisera la communauté en deux. Tous les exemples de concombre ne fonctionneront pas dans toutes les installations et les documents seront dupliqués en deux. Pas chic.

Je ne suis pas sûr des soucis de changement de rupture, la v2 est le moment idéal pour introduire ce genre de changement. Attendre forcera à faire/attendre un autre majeur.

Que diriez-vous de changer les Before et After pour injecter world :

defineSupportCode(function({After, Before, Given}) {
  let world;

  // Asynchronous Callback
  Before(function (w, scenarioResult, callback) {
    world = w;
    callback();
  });
  After(function (world, scenarioResult, callback) {
    callback();
  });

  Given('I access the world', () => {
    assert(world); // Yay!
  });
});

De cette façon, vous pouvez capturer le world dans une variable et une étape before . Cela ne gâchera pas chaque définition d'étape.

Ensuite, nous gardons l'ancienne méthode de travail mais générons un message deprecated . Ou même ne le faites pas, laissez simplement la possibilité aux personnes qui souhaitent utiliser cette approche et savent ce qu'elles font.

Je crains que l'argument sur la compatibilité des extraits trouvés en ligne ne soit un argument convaincant.

Même s'il y aura des drapeaux --ui , ou setWorldInjectionStrategy('argument') - cela devient un piège qui est mieux communiqué en tant que changement de rupture d'une version majeure, servi avec toutes les discussions en ligne et le tumulte digne de tels changements, et aider à éliminer la confusion.

Donc je vote pour le faire dans la prochaine version de cuke, et... makedoing jusqu'à sa sortie.

Une version canari ou un drapeau caché serait une promotion géniale que j'utiliserais et que j'utiliserais dès le début

J'aimerais exposer une interface FP alternative.

Qu'en est-il de ce qui suit ? (en utilisant async/wait pour illustrer c'est aussi compatible avec les promesses)

import {initialize, Given, Before} from 'cucumber/fn'

// specify a function that returns the initial context:
initialize(async () => ({ a: 42 }))

Before({ timeout: 10 }, async (ctx) => {
  await doStuff(ctx.a)
})

Given(/^a step passes with {number}$/, async (ctx, number) => {
  const newA = await computeStuff(ctx.a, number)
  // tell cucumber about the new context:
  return Object.assign({}, ctx, { a: newA })
})

J'ai piraté cucumber-fp qui offre des définitions d'étapes fonctionnelles. J'ai déjà quelques idées d'amélioration. Commentaires bienvenus!

Je suggère que nous fermions ce problème et que les gens expérimentent cette petite bibliothèque. Peut-être qu'un jour nous pourrons l'intégrer dans Cucumber.js.

@jbpros Je préférerais ne pas avoir à installer une autre dépendance juste pour pouvoir utiliser les fonctions fléchées dans mes tests.

Cela devrait en fait être très simple à mettre en œuvre pour tout le monde. L'extrait de code suivant fonctionne plutôt bien (fonctionnellement) pour moi, avec une énorme mise en garde qui le rend inutilisable :

// Don't rely on `this` in step definitions. It's 2021 for crying out loud.
const definitionFunctionWrapper = (fn) =>
    function(...args) {
        return fn(...args.slice(0, -1), this);
    }

Cette mise en garde étant que chaque définition d'étape enregistre désormais l'erreur suivante en raison du paramètre supplémentaire :

    Error: function uses multiple asynchronous interfaces: callback and promise
       to use the callback interface: do not return a promise
       to use the promise interface: remove the last argument to the function

Si vous essayez d'ajouter des paramètres supplémentaires pour contourner ce problème, vous obtenez

function has 3 arguments, should have 1 (if synchronous or returning a promise) or 2 (if accepting a callback)

Tout ce dont cucumber-js a besoin est une option pour désactiver les vérifications des arguments de fonction et ce problème disparaîtra. En fait, à la réflexion, il semble que le test expirera également, car le concombre suppose qu'un rappel est requis en fonction du nombre d'arguments dans la fonction de définition d'étape. Cela pourrait être contourné en donnant la priorité à une promesse retournée, cependant.

@andyearnshaw merci pour votre contribution, j'entends votre préoccupation concernant une dépendance "juste pour les fonctions de flèche dans stepdefs". Cette bibliothèque est essentiellement la solution que j'utilise personnellement pour obtenir des définitions d'étape sans état, je l'ai juste empaquetée pour toute personne intéressée.

Considérez-le comme une solution temporaire autour du débat en cours pour une API stepdef aussi pure dans le noyau (cela dure depuis plus de 4 ans, croyez-le ou non). Comme je l'ai dit, il s'agit d'une expérience qui pourrait atterrir dans le noyau à un moment donné et j'apprécierais vraiment les commentaires des personnes qui l'utilisent réellement. S'il obtient suffisamment de traction, cela constituerait un meilleur argument pour l'intégration dans le concombre.

Veuillez également noter qu'il propose quelques autres (petits) outils fonctionnels utiles : tap() et des contextes en lecture seule imposés.

Le contrôle d'arité sur les fonctions stepdef est définitivement un problème que j'ai dû contourner dans cette lib (d'une manière assez moche). Une option pour le désactiver à la fois sur l'interface de ligne de commande et par programmation serait très utile pour cela (et potentiellement d'autres cas d'utilisation). J'adorerais faire ça, mais le temps est une ressource rare pour moi en ce moment.

N'hésitez pas à vous inspirer de cucumber-fp pour corriger le contrôle d'arité en attendant.

@jbpros c'est super, et j'apprécie vraiment les efforts que vous avez déployés. J'étais plus en désaccord avec le sentiment que cette question devrait être close. Je vais jeter un œil à votre bibliothèque et voir si cela m'aide à contourner ce contrôle ennuyeux. ??

@andyearnshaw a raison, merci pour la clarification. Je suis d'accord que nous ne devrions pas abandonner cette idée et garder cette question ouverte est probablement un bon moyen de garder les choses transparentes, en effet.

Cette page vous a été utile?
0 / 5 - 0 notes