Cucumber-js: Solicitação de recurso: UI que não usa a palavra-chave `this`

Criado em 31 jan. 2017  ·  16Comentários  ·  Fonte: cucumber/cucumber-js

Dado um mundo como:

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

Eu quero ser capaz de escrever:

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

ao invés de

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

Isso também me livrará do monkeying de .bind(this) em todas as operações assíncronas ou, se você preferir, de usar o monkeying de var that = this .

Mais um benefício (IMHO) - vai ajudar as pessoas a sair da árvore de herança para mundos e orientá-las melhor para os melhores poderes de JS

accepted enhancement

Comentários muito úteis

OK. Como nem todo mundo é programador funcional e para evitar a introdução de uma mudança brusca, o que vocês acham de fazer uma opção que faz com que o mundo seja passado como o primeiro parâmetro?

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

Todos 16 comentários

Eu concordo que isso significará que você sempre aceitará world como argumento, e isso é uma mudança de API.
Eu acho que assim como o mocha que tem --ui exports e --ui tdd e --ui bdd - o cucue também pode.
É basicamente uma variável de projeto que determina se as etapas são chamadas / aplicadas no mundo, ou a aceita como primeiro argumento.

Eu realmente não acho que isso valeria a pena no longo prazo. Sim, é bom para as etapas de 1 linha em que você consegue usar => onde, de outra forma, você não conseguiria. Em minha experiência, dei muito poucos passos como este. Estou surpreso que o ES6 não incluiu uma função de seta que não retém o contexto, pois isso seria ideal para mim.

Isso também me livrará do monkeying de .bind(this) em todas as operações assíncronas ou, se você preferir, de usar o monkeying de var that = this .

Você não pode usar a seta grande dentro das definições de etapa para evitar isso?

Mais um benefício (IMHO) - vai ajudar as pessoas a sair da árvore de herança para mundos e orientá-las melhor para os melhores poderes de JS

Você pode explicar isso um pouco mais / dar um exemplo

Em palavras simples: eu pertenço a uma escola de programadores que evitou a maneira de frasear um contexto como uma classe. Quanto mais pessoas ficam expostas ao lado negro da OOP, essa escola fica maior - e o movimento na comunidade JS que impediu a função de flecha de reter o contexto é um exemplo da força desse movimento.

Sem entrar em um debate religioso - vou apenas dizer que acredito em viver e deixar viver, você quer usar as aulas? excelente. Por favor , não me force a :-)

Meu sentimento geral é que poderia ser uma pequena mudança, e ficarei feliz em fazê-lo se você puder me fornecer a direção geral de como você acha que seria feito melhor ;-)

Quando estamos definindo etapas, estamos definindo funções, não classes, então logicamente this não tem lugar nisso.

Eu acho que esta é uma situação para o paradigma FP ao invés do paradigma OOP.

@osher Acho que podemos dar a @charlierudolph o benefício da dúvida de que ele não quer forçá-lo a usar classes. Dito isso, ele não deve ser forçado a integrar (interromper) alterações de API sempre que estiver usando este pacote, a menos que ele ofereça alguma vantagem tangível.

@charlierudolph Acho que por "os melhores poderes do JS" @osher significa FP, Programação Funcional.

IMHO, essa mudança tornaria as definições de etapas um pouquinho mais simples e lógicas. Basicamente, ele removeria esta linha: const thisWorldNotThisStep = this

Definitivamente, não há necessidade de adicionar .bind(this) a todas as suas funções assíncronas e, contanto que dentro da definição de etapa você esteja usando funções de seta, não há necessidade de usar o padrão const self = this .

O problema (que não é um grande problema) é que dentro de uma definição de etapa, this se refere ao mundo, não à etapa, o que é contra-intuitivo.

OK. Como nem todo mundo é programador funcional e para evitar a introdução de uma mudança brusca, o que vocês acham de fazer uma opção que faz com que o mundo seja passado como o primeiro parâmetro?

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

Concordo totalmente com a ideia de injetar o mundo / contexto tem um parâmetro da etapa. A partir deste projeto, todo o ecossistema JS está se movendo para abraçar totalmente as novas funcionalidades permitidas pelo ES6 e isso é uma coisa boa ❤️

Se você olhar para outras ferramentas como Express ou Koa, elas definem o contexto da solicitação atual como this AND como o primeiro parâmetro do middleware (é o equivalente à etapa pepino). Esta solução permite o uso tradicional da função e o uso da função de seta ES6.
Outra vantagem do contexto em como o primeiro parâmetro é que

Para a solução proposta por @charlierudolph , não acho que isso funcione a longo prazo: isso vai dividir a comunidade em duas. Todos os exemplos de pepino não funcionarão em todas as instalações e os documentos serão duplicados em duas. Nada extravagante.

Não tenho certeza sobre as preocupações com mudanças significativas, a v2 é o momento perfeito para introduzir esse tipo de mudança. A espera forçará a fazer / esperar por outro curso.

Que tal mudarmos Before e After para injetar 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!
  });
});

Dessa forma, você pode capturar o world em uma variável e um passo before . Não vai atrapalhar a definição de cada etapa.

Em seguida, mantemos a maneira antiga de trabalhar, mas geramos uma mensagem deprecated . Ou ainda não faça isso, apenas deixe a opção lá para as pessoas que querem usar essa abordagem e saber o que estão fazendo.

Receio que o argumento sobre a compatibilidade de snippets encontrados online seja um argumento convincente.

Mesmo se houver --ui sinalizadores, ou setWorldInjectionStrategy('argument') - torna-se um pegadinho que é melhor comunicado como uma mudança radical de uma versão principal, servida com todas as discussões online e tumulto condizente com tais mudanças, e ajudando a eliminar confusão.

Então eu voto por fazer isso na próxima versão do cuke, e ... makedoing até que seja lançado.

Uma versão canário ou uma bandeira oculta seria uma promoção incrível que eu usaria e receberia feedback desde o início

Adoraria expor uma interface FP alternativa.

E quanto ao seguinte? (usando async / await para ilustrar que é compatível com promessas também)

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 })
})

Eu hackeado pepino-fp que oferece definições de etapas funcionais. Já tenho algumas idéias de melhoria. Feedback seja bem-vindo!

Sugiro que encerremos este problema e deixemos que as pessoas experimentem essa pequena biblioteca. Talvez um dia possamos trazê-lo para Cucumber.js.

@jbpros Eu prefiro não ter que instalar outra dependência apenas para poder usar as funções de seta em meus testes.

Isso deveria ser realmente simples para que todos pudessem implementar por si próprios. O seguinte snippet de código funciona muito bem (funcionalmente) para mim, com uma grande ressalva que o torna inutilizável:

// 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);
    }

Essa ressalva sendo que cada definição de etapa agora registra o seguinte erro por causa do parâmetro adicional:

    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

Se você tentar adicionar parâmetros adicionais para contornar isso, você obterá

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

Tudo o que o cucumber-js precisa é uma opção para desabilitar as verificações dos argumentos da função e este problema irá embora. Na verdade, pensando bem, parece que o tempo do teste expirará também, pois o cucumber assume que um retorno de chamada é necessário com base no número de argumentos na função de definição de etapa. No entanto, isso poderia ser contornado dando-se precedência a uma promessa devolvida.

@andyearnshaw obrigado por sua entrada, ouvi sua preocupação sobre uma dependência "apenas para funções de seta em stepdefs". Esta biblioteca é basicamente a solução que eu uso pessoalmente para obter definições de etapas sem estado, eu apenas a empacotei para qualquer pessoa interessada por aí.

Considere isso como uma solução temporária em torno do debate em andamento para uma API stepdef pura no núcleo (ela já dura há mais de 4 anos, acredite ou não). Como eu disse, este é um experimento que pode atingir o núcleo em algum ponto e eu realmente gostaria de receber feedback das pessoas que realmente o estão usando. Se conseguir tração suficiente, seria um argumento melhor para a integração no pepino.

Além disso, observe que ele está oferecendo algumas outras (pequenas) ferramentas funcionais úteis: tap() e contextos somente leitura obrigatórios.

A verificação de aridade nas funções do stepdef é definitivamente um problema que eu tive que contornar nesta biblioteca (de uma maneira bastante desagradável). Uma opção para desligá-lo na CLI e programaticamente seria muito útil para isso (e potencialmente para outros casos de uso). Adoraria fazer isso, mas o tempo é um recurso escasso para mim no momento.

Sinta-se à vontade para se inspirar no pepino-fp para corrigir a verificação de aridade entretanto.

@jbpros isso é ótimo, e eu realmente aprecio o esforço que você fez aqui. Eu estava mais em desacordo com o sentimento de que essa questão deveria ser encerrada. Vou dar uma olhada em sua biblioteca e ver se isso me ajuda a contornar essa verificação irritante. 🙂

@andyearnshaw ha certo, obrigado pelo esclarecimento. Concordo que não devemos abandonar essa ideia e manter esse problema em aberto é provavelmente uma boa maneira de manter as coisas transparentes, de fato.

Esta página foi útil?
0 / 5 - 0 avaliações