Jest: Meta: suporte nativo para módulos ES

Criado em 19 jan. 2020  ·  131Comentários  ·  Fonte: facebook/jest

EDIT: guia rápido para começar: https://jestjs.io/docs/en/ecmascript-modules

O suporte ESM será sem sinalização em uma versão futura do Nó 12 (talvez não antes de abril https://github.com/nodejs/node/pull/29866#issuecomment-574055057) e já não está sinalizado no Nó 13.2, então acho que é hora de avaliar como podemos adicionar suporte nativo no Jest. Tentarei listar quais recursos Jest fornece atualmente que são afetados pelo suporte do ESM e como podemos resolvê-los / investigá-los.

Há o problema nº 4842, mas acho que é mais um problema de discussão, embora esse problema seja voltado para a implementação de suporte e mais adequado para rastrear para aqueles que desejam apenas obter o status de implementação atual. Quaisquer comentários adicionados a este problema _não_ relacionados a como podemos implementar suporte para os recursos enumerados abaixo serão marcados como spam - direcione quaisquer soluções alternativas / discussões para problemas separados. Sinta-se à vontade para nos dizer se algo relacionado aos recursos do ESM estiver faltando na lista!

Observe que Jest usará a API vm (https://nodejs.org/api/vm.html) e no momento da escrita (nó v13.6) as partes ESM desta API ainda estão sinalizadas ( --experimental-vm-modules ). Portanto, dizer que ESM não é sinalizado é um pouco incorreto no momento. Mas acho que devemos começar a experimentar e, potencialmente, fornecer feedback para o WG de Módulos .

Por último, estou escrevendo esta edição principalmente para pessoas que implementarão o suporte, portanto, será um pouco de baixo nível e específico para o funcionamento do Jest. Para as pessoas que _just_ querem saber se o suporte foi recebido ou não, recomendo usar a maravilhosa "notificação personalizada" do GH e se inscrever apenas para receber notificações no fechamento / reabertura.


  • [x] Executando o módulo no contexto correto

Alcançamos sandboxes executando um script dentro de um determinado vm.Context (fornecido por JSDOM ou APIs de núcleo de nó). Precisamos fazer o mesmo para o ESM, mas precisaremos acessar context durante a construção do módulo, não apenas durante a execução do módulo. Abri o # 9428, que adiciona as APIs necessárias a JestEnvironment .

  • [x] Globais

expect , test , beforeEach etc ainda serão adicionados como globais, nada deve mudar aqui. jasmine global também estará aqui.

  • [x] jest propriedade "global"

Este não é realmente um global - é injetado no escopo do módulo. Como o escopo do módulo foi eliminado no ESM, precisamos movê-lo para algum lugar. Adicioná-lo a import.meta parece natural - há uma opção chamada initializeImportMeta que podemos usar.

EDITAR: a solução aqui é buscá-lo por meio de import {jest} from '@jest/globals' . Ainda podemos adicioná-lo por meio de import.meta no futuro, mas isso deve ser o suficiente por enquanto.

  • [] jest.(do|un)mock

Como o ESM tem diferentes "estágios" ao avaliar um módulo, jest.mock não funcionará para importações estáticas. No entanto, ele pode funcionar para importações dinâmicas, então acho que só temos que deixar claro nos documentos o que ele suporta e o que não é.

jest.mock chamadas são suspensas, mas isso não ajuda no ESM. Podemos considerar a transformação de import 'thing' em import('thing') que deve permitir que o içamento funcione, mas é assíncrono. Usar await é provavelmente uma necessidade para tal abordagem. Também acho que é invasivo o suficiente para justificar uma opção separada. Algo para discutir - não precisamos oferecer suporte a tudo que jest.mock pode para um lançamento inicial.

  • [] jest.requireActual

Não tenho certeza se como ele deve se comportar no ESM. Devemos fornecer jest.importActual e deixar requireActual avaliar em CJS sempre?

  • [x] import.meta

O nó tem url como sua única propriedade (pelo menos por enquanto). Precisamos ter certeza de que ele está preenchido em Jest também. Fornecemos identifier vez de filename ao construir o módulo, então não acho que isso acontecerá automaticamente, mas url é essencialmente filename passado por pathToFileURL .

Também há um PR aberto para import.meta.resolve : https://github.com/nodejs/node/pull/31032

  • [x] import thing from 'thing'

Isso deve ser bastante simples, só precisamos implementar linker onde também podemos transformar a fonte antes de retorná-la, o que significa que não precisamos da API do carregador (que ainda não existe). Isso nos permite retornar simulações também (embora elas tenham que vir de um diretório __mocks__ ).

  • [x] import('thing')

Essencialmente o mesmo que acima, mas passado como importModuleDynamically ao construir o módulo. Também suportará jest.mock , jest.resetModules etc de maneira mais limpa, portanto, provavelmente será usado um pouco.

Isso também pode ser feito por vm.Script por meio da mesma opção.

  • [] Tratamento de erros durante a avaliação

No momento, é um erro de tempo de execução (por exemplo, módulo não encontrado), mas isso não é necessariamente verdadeiro com o ESM. Isso importa para nós? Devemos verificar se os erros ainda parecem bons.

  • [x] module.createRequire

Precisamos lidar com isso para as pessoas que desejam usar o CJS do ESM. Abri o número 9426 para rastrear isso separadamente, pois implementá-lo não está realmente relacionado ao suporte ESM.

EDITAR: Implementado em # 9469

  • [] module.syncBuiltinESMExports

https://nodejs.org/api/modules.html#modules_module_syncbuiltinesmexports. Nós nos preocupamos com isso, ou apenas torná-lo um ambiente autônomo o suficiente? Não tenho certeza de qual seria o caso de uso em Jest. Mexer com os embutidos já está quebrando a caixa de areia e eu não acho que isso deveria importar.

EDIT: # 9469 tornou isso um autônomo. Eu acho que está bom?

  • [] Detectar se um arquivo deve estar no modo ESM ou CJS

Inspecionar o campo type no package.json um módulo parece razoável: https://nodejs.org/api/esm.html#esm_enabling. Devemos também ter nosso próprio sinalizador de configuração? Também deve respeitar as terminações do arquivo.

https://github.com/nodejs/modules/issues/393

  • [x] moduleNameMapper

Não tenho certeza se isso afeta alguma coisa. _Acho_ que não, já que ligaremos os módulos nós mesmos. Precisa de investigação, no entanto.

EDIT: Esta é toda a lógica de resolução, que controlamos. Portanto, nenhuma mudança aqui.

  • [x] jest.config.mjs

Por meio do # 9291, apoiamos jest.config.cjs - precisamos fazer algo especial para .mjs ? Provavelmente use import('path/to/configFile.mjs') que significa que terá que ser assíncrono. Isso é um problema? Pode valer a pena fazer a resolução de configuração async no Jest 25, portanto, não é um bloqueador para suporte incremental do ESM no Jest 25.

EDITAR: # 9431

  • [] Exportações de Pacotes

O Node suporta exportações de pacote , que são mapeadas para moduleNameMapper Jest, mas também fornece recursos de encapsulamento. Esperançosamente resolve irão implementar isso, mas se não, precisaremos fazer algo. Pode ser o suficiente para usar a opção pathFilter ? Não tenho certeza.

  • [] Módulo JSON / WASM

https://nodejs.org/api/esm.html#esm_experimental_json_modules. Precisamos nos preocupar? Provavelmente, especialmente por json . É trivial para nós oferecer suporte a import thing from './package.json' uma vez que controlamos a fase de vinculação, mas provavelmente não devemos fazer isso por padrão, pois será diferente do nó padrão. Devemos forçar as pessoas a definir uma transformação para isso?

  • [x] Cobertura de código

Isso importa? Não acho que seja afetado, pois ainda podemos transformar a fonte com babel (talvez seja confundido por import instruções, provavelmente não) e a cobertura V8 definitivamente não deveria se importar. Devemos verificar embora.

  • [] Resolução de código assíncrono

Isso não é absolutamente nenhum bloqueador, pois a resolução de sincronização funcionará perfeitamente. Mas nós _podemos_ usar a resolução assíncrona agora, o que é ótimo. Eu me pergunto se devemos olhar apenas para usar o módulo resolve fora do npm novamente, pois ele já oferece suporte ao assíncrono. Consulte # 9505.

  • [] Transformação de código assíncrono

Semelhante ao anterior, sem bloqueio, mas seria bom apoiá-lo. Pode tornar @jest/transformer mais utilizável em outros ambientes também. Veja # 9504.

  • [] Mau desempenho ao acessar globais

Devido ao # 5163, temos a opção extraGlobals como uma solução alternativa - essa solução alternativa não é mais viável no ESM. Abri um problema com o nó aqui: https://github.com/nodejs/node/issues/31658

ES Modules

Comentários muito úteis

Consegui suporte básico com o # 9772. Eu testei apenas os casos mais simples, e há muitas limitações conhecidas (principalmente sem suporte a objeto jest e semântica quebrada ao misturar CJS e ESM), mas pelo menos é _algo_. Ele será lançado na próxima versão do Jest (espero que em breve, apenas bloqueado por # 9806)

Todos 131 comentários

Consegui suporte básico com o # 9772. Eu testei apenas os casos mais simples, e há muitas limitações conhecidas (principalmente sem suporte a objeto jest e semântica quebrada ao misturar CJS e ESM), mas pelo menos é _algo_. Ele será lançado na próxima versão do Jest (espero que em breve, apenas bloqueado por # 9806)

25.4.0 foi lançado com as primeiras peças de suporte. Além do # 9772 mencionado acima, também incluí o # 9842. Na _teoria_, a mistura de CJS e ESM deve funcionar corretamente agora (🤞).

O único recurso que falta é o suporte ao objeto jest . Ainda não decidi se devemos mantê-lo em import.meta ou exigir que as pessoas o importem através de import {jest} from '@jest/globals' . Agradecemos o feedback!

Eu não escrevi documentos para isso ainda, mas para ativá-lo, você precisa fazer três coisas

  1. certifique-se de não executar as instruções transform import (defina transform: {} na configuração ou então certifique-se de que babel não transforme o arquivo em CJS, evitando o modules opção para predefinir o env)
  2. Execute node@^12.16.0 || >=13.2.0 com a bandeira --experimental-vm-modules
  3. Execute seu teste com jest-environment-node ou jest-environment-jsdom-sixteen

Experimente e forneça comentários! Se relatar bugs, seria maravilhoso se você também pudesse incluir como a execução do mesmo código (sem qualquer código específico de teste) é executado no Node. Eu li https://nodejs.org/api/esm.html _muito_ nas últimas semanas, mas provavelmente deixei passar algo.

A única característica que falta é apoiar o objeto de brincadeira. Ainda não decidi se devemos colar em import.meta ou exigir que as pessoas importem por meio de import {jest} de '@ jest / globals'.

Para o caso de uso de texto digitado, é melhor ter uma importação explícita.

Sim, eu adicionei (e reverti temporariamente) um pacote @jest/globals que suporta isso, então estará disponível de qualquer maneira. Estou me perguntando se faz sentido _também_ expô-lo em import.meta . Atualmente inclinado a não fazer isso, principalmente porque é mais fácil adicionar do que remover depois (e, pessoalmente, não sou fã de globais)

+1 para a importação explícita, é um pouco mais prolixo, mas mais simples de entender

Estou recebendo isso no Nó 13.2 e Jest 25.4: ES Modules are only supported if your test environment has the getVmContext function O que estou perdendo?

@zandaqo Oh, desculpe, esqueci esse ponto. Adicionado acima, mas é

Execute seus testes com jest-environment-node ou jest-environment-jsdom-sixteen

ReferenceError: jest is not defined Suponho que isso seja devido à falta de @jest/globals

Sim, conforme mencionado, isso só funcionará se você não usar o objeto jest .
Provavelmente, as simulações também estão quebradas, não as testei 😃

Compilei um projeto muito básico a partir do que vejo no diretório de testes e2e ( e2e/native-esm/__tests__/native-esm.test.js ) e nesta edição. E, infelizmente, ainda não consigo fazer funcionar 🙃Alguém pode verificar por acaso?

https://drive.google.com/file/d/1vyDZjsVKOTu6j55QA11GjO9E7kM5WX8_/view?usp=sharing

  • [x] jest versão 25.4.0
  • [x] versão do nó v13.12.0
  • [x] package.json contém opções de jest recomendadas e nenhuma transformação de babel parece estar em vigor

Executando script de amostra (apenas importando double function e imprimindo double(2) ):

npm run main

> [email protected] main /Users/ilya/Projects/jest-esm
> node src/main.js

(node:16961) ExperimentalWarning: The ESM module loader is experimental.
4

Executando o jest com apenas um teste para função dupla:

npm run test

> [email protected] test /Users/ilya/Projects/jest-esm
> jest

 FAIL  __tests__/native-esm.test.js
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /Users/ilya/Projects/jest-esm/__tests__/native-esm.test.js:8
    import {double} from '../src/index.js';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      at Runtime._execModule (node_modules/jest-runtime/build/index.js:1074:58)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.542s
Ran all test suites.

Você precisa executar o nó com --experimental-vm-modules e nomear o arquivo .mjs ou "type": "module" em package.json .

EDIT: Você provavelmente tem o último visto que funciona fora do Jest para você, faltando apenas o sinalizador experimental para Node

@SimenB oh uau, existem --experimental-vm-modules E --experimental-modules . Fiquei confuso com o fato de que --experimental-modules não é necessário a partir de alguma versão do nó 13. Obrigado!

TLDR: node --experimental-vm-modules node_modules/jest/bin/jest.js funciona para mim

Sim, do OP

Observe que Jest usará a API vm (https://nodejs.org/api/vm.html) e no momento da escrita (nó v13.6) as partes ESM desta API ainda estão sinalizadas ( --experimental-vm-modules ). Portanto, dizer que ESM não é sinalizado é um pouco incorreto no momento.

É incrível que funcione para você, no entanto!

(Vou marcar esses comentários como resolvidos)

@SimenB Obrigado por isso! Dois problemas que estou vendo até agora.

Problema 1

  • O arquivo de teste de unidade ESM importa o padrão do arquivo ESM (fn sendo testado)
  • O arquivo ESM sendo testado importa o padrão de um pacote, que exporta apenas CJS
  • Resultados em erro: ReferenceError: module is not defined no arquivo do pacote CJS

Então, estou pensando que isso pode não estar implementado corretamente?

Uma instrução de importação pode fazer referência a um módulo ES ou um módulo CommonJS

Isso funciona bem ao executar o aplicativo. As exportações nomeadas do CJS só podem ser importadas usando createRequire, mas as exportações padrão podem apenas ser importadas.

Edição 2

Quando não estou acertando o erro acima, estou acertando este:

TypeError: _vm(...).SyntheticModule is not a constructor

      at Runtime._importCoreModule (node_modules/jest-runtime/build/index.js:1198:12)

Detalhes do Projeto

  • Nó 12.14.1
  • Jest e babel-jest 25.4.0
  • Babel mais recente com targets: { node: 12 } e 2 plug-ins: babel-plugin-transform-import-meta e rewire-exports . (Tentei remover o plugin import-meta mas ocorreu um erro dizendo para adicioná-lo novamente.)
  • testEnvironment: "node" é praticamente a única configuração
  • node --experimental-vm-modules node_modules/jest/bin/jest.js

Se um repositório de reprodução for útil, me avise.

Obrigado @aldeed!

Vou analisar a questão 1, que realmente parece um bug. EDITAR: Deve ser corrigido via # 9850

O problema 2 precisa do nó 12.16.0: https://nodejs.org/docs/latest-v12.x/api/vm.html#vm_class_vm_syntheticmodule

Vou mudar a verificação no Jest (agora ele verifica vm.SourceTextModule que está disponível em mais versões, mas precisamos de SyntheticModule também).

Confirmei que executar com 12.16.0 corrige meu problema 2. Testarei novamente o problema 1 depois que a correção for lançada. Caso contrário, aguardando o objeto jest para mais testes, e eu concordo que ele deve precisar ser importado.

Excelente trabalho, @SimenB! Estou tentando fazer isso em um pequeno projeto, mas tive problemas com importações dinâmicas. Este é o erro que estou vendo:

Module status must not be unlinked or linkingError [ERR_VM_MODULE_STATUS]: Module status must not be unlinked or linking

Se eu remover as importações dinâmicas, o teste será executado (mas falhará por outros motivos, é claro). O mesmo teste está funcionando atualmente com o Mocha (que forneceu suporte ESM recentemente).

Se ajudar, as importações dinâmicas em questão podem ser vistas aqui: https://github.com/beejunk/firn.js/blob/switch-to-jest/lib/renderPage.js#L43 -L55

O arquivo de teste está aqui: https://github.com/beejunk/firn.js/blob/switch-to-jest/test/build.test.js. A versão funcional do Mocha pode ser vista no branch master.

Avise-me se houver alguma outra informação que possa ser útil.

Obrigado @beejunk! Eu estive pensando se poderia haver condições de corrida entre import s que importam o mesmo módulo antes de estar totalmente vinculado. _Parece_ que é isso que você está acertando aqui. Vou consertar isso hoje, obrigado pelo relatório!

EDITAR: corrigido em # 9858. Copiou a correção para o seu repo:
image

Alguém conseguiu fazer isso funcionar com o TypeScript? node --experimental-vm-modules node_modules/jest/bin/jest.js retorna o mesmo SyntaxError: Cannot use import statement outside a module , embora meu package.json tenha "type": "module" .

babel.config.cjs

module.exports = {
  presets: [
    '@babel/preset-typescript',
  ],
};

jest.config.cjs

module.exports = {
  testEnvironment: 'jest-environment-node',
  transform: {},
};

@dandv Você está basicamente acertando este caso: https://github.com/facebook/jest/pull/9772/files#r407255029

Você poderia abrir um problema separado? Precisará descobrir o que fazer com extensões não-js

@SimenB : # 9860. Obrigado por dar uma olhada.

@aldeed @beejunk 25.5.0 foi lançado com correções para seus problemas. Continue com os relatórios de bug 😀

Além disso, inclui suporte para import { jest } from '@jest/globals' para aqueles que estão esperando por isso 👍

Obrigado pelo trabalho rápido em tudo isso, @SimenB! Acho que encontrei outro problema.

Tenho feito experiências com o uso de parâmetros de consulta em um caminho de importação como uma forma de invadir o cache do módulo em um servidor de desenvolvimento. A ideia é ter um inspetor de arquivos que detecta mudanças em um componente e então atualiza o caminho de importação com um parâmetro de consulta arbitrário para que o novo código seja imediatamente puxado na próxima página carregada sem ter que reiniciar o servidor. Isso funciona ao executar o servidor de desenvolvimento. No entanto, Jest está gerando o seguinte erro:

Cannot find module '/path/to/project/components/BasePage.js?cache=0' from 'renderPage.js'

Aqui está um exemplo de onde os parâmetros de consulta estão sendo usados: https://github.com/beejunk/firn.js/blob/switch-to-jest/lib/renderPage.js#L55 -L56

Se eu remover os parâmetros da consulta, os testes serão aprovados, embora não de forma consistente. Se eu executar um único pacote (por exemplo, npm test -- test/build.test.js ), os testes serão aprovados, mas se eu executar todos os testes de uma vez, eles falharão na maioria das vezes com erros ambíguos. Ainda estou investigando o problema com os testes inconsistentes, mas pensei em relatar o problema do parâmetro de consulta primeiro.

Obrigado pelo relatório @beejunk. # 6282 deve lidar com isso, mas também quer passar a consulta para transformadores e outras coisas, de que não precisamos aqui. Portanto, pode fazer sentido apenas suportar a consulta internamente no tempo de execução por enquanto, e deixar # 6282 lidar apenas com a transmissão dessa consulta.

Eu adicionei um pouco de código para fazer o cache do módulo variar por consulta: https://github.com/facebook/jest/blob/d425a49bd575e7167bc786f3c4f2833589091fa1/packages/jest-runtime/src/index.ts#L330 -L334

Mas nenhum código jamais o chama com uma consulta. Acho que devemos ser capazes de fazer apenas resolvePath.split('?') e tudo deve funcionar.

Com relação aos erros inconsistentes, vou dar uma olhada se aquele repo os reproduz. Não testei o código ESM com testes em paralelo, apenas um único teste. Não sei por que isso afetaria as coisas, mas quem sabe 😀

O item de consulta

Tenho um problema que acho que pode estar relacionado a isso, mas não foi corrigido em 25.X .

Vou tentar resumir o cenário abaixo:

  • Você tem um teste com um script de configuração
  • Esse script de configuração exigirá um arquivo dinamicamente, como em:
    { default: generator } = require(path.resolve(f))
  • Tudo dentro de f não é transpilado causando o "erro inesperado de importação de identificador".

Isso também acontece se eu tentar com import ()

Já que você mencionou a transpilação; se você tiver uma configuração que transpila import em require esse problema não é o lugar correto - esse problema é sobre o suporte nativo.


Dito isso - você não pode require ESM, e eu não fui capaz de adicionar suporte para import() do CJS ainda porque o Node não tem API para ele. O suporte para isso chegou ao Node master, mas ainda não foi lançado: https://github.com/nodejs/node/pull/32985. Como pode ser visto nos PRs de lançamento vinculados, ele virá em 13.14.0 e 14.1.0. Nesse ponto, implementarei suporte para ele 👍

Você deve ser capaz de usar um arquivo de configuração .mjs .

@SimenB Isso é ótimo, parece estar funcionando em vários arquivos de teste agora! É um pouco complicado resolver as diferenças entre as importações do Babel e do Node em cada arquivo, adicionar a importação jest etc., portanto, posso encontrar mais problemas ao fazer isso em centenas de arquivos de teste.

Algumas coisas que são mais perguntas:

  • o que você disse em seu comentário anterior sobre o suporte para import() de cjs, isso também permitirá que o arquivo de configuração do Jest seja nomeado jest.config.js ? Atualmente, só funciona para mim quando denominado jest.config.cjs e usando module.exports = (o erro é TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified )
  • O nome "@ jest / globals" falha na regra eslint node/no-extraneous-import devido a não ser um pacote listado em package-lock . Existe algum motivo para essa convenção de nomenclatura? É possível usar jest sem o @ ? É fácil ignorar a regra, mas estou apenas pensando.
  • Pergunta relacionada, por que jest global é diferente de fns mágicos como test , describe , before , etc.? Todos esses devem ser importados agora também?
  • Quando isso for finalizado, Jest poderá definir a bandeira --experimental-vm-modules ? Isso meio que faz com que isso pareça fora do padrão se não funcionar apenas com o comando jest . Se você não puder passar sinalizadores diretamente, possivelmente pode definir / alterar a variável NODE_OPTIONS env?
  • Parece que agora recebo um erro um pouco diferente do que antes ao tentar isso em um Node mais antigo, mas nenhum dos erros ficou claro de que a versão mínima do Node era o problema. É fácil adicionar uma verificação da versão mínima com uma mensagem de erro mais útil?

isso também permitirá que o arquivo de configuração do Jest seja nomeado jest.config.js?

Ele pode ser denominado .js se seu mais próximo package.json tiver type: 'module' . Caso contrário, para usar o ESM no arquivo de configuração, ele precisa ser nomeado .mjs . Consulte https://nodejs.org/api/esm.html#esm_enabling. Observe que o arquivo de configuração é executado fora do Jest, portanto, não temos muito controle. No momento, não oferecemos suporte à configuração assíncrona, mas await ing uma promessa exportada seria trivial, PR welcome 🙂 # 8357 estagnou.

Estou muito surpreso que você receba A dynamic import callback was not specified , no entanto, não carregamos o arquivo de configuração em uma VM ... Você poderia abrir um problema separado?

O nome "@ jest / globals" falha na regra eslint node/no-extraneous-import devido a não ser um pacote listado em package-lock . Existe algum motivo para essa convenção de nomenclatura? É possível usar jest sem o @ ? É fácil ignorar a regra, mas estou apenas pensando.

Você deve adicionar devDependency em @jest/globals . O pacote em si é apenas definições de tipo. É um pacote separado de jest para que as definições de tipo funcionem corretamente e, portanto, podemos lançar um erro se for carregado fora do tempo de execução do Jest.

No momento, não tenho planos de mudar isso, mas podemos suspender o uso desse pacote e exportar os tipos de jest . Essa é uma mudança importante, então vamos ver mais tarde 🙂

Pergunta relacionada, por que jest global é diferente de fns mágicos como test , describe , before , etc.? Todos esses devem ser importados agora também?

jest é como require ou module objeto do CJS no sentido de que é único por arquivo, não é realmente um global (ou seja, globalThis.jest === undefined ). Isso permite que jest.mock('../../file.js') etc funcione corretamente em relação ao arquivo em que foi injetado. Você pode escolher importar também os globais reais, mas eles ainda estão disponíveis como globalThis.expect etc.

Quando isso for finalizado, Jest poderá definir a bandeira --experimental-vm-modules ?

Acho que vamos esperar que o nó os desmarque em vez de configurá-los silenciosamente - eles estão sinalizados por um motivo, a API pode mudar em algum ponto. Poderíamos adicionar uma opção que defina NODE_OPTIONS Eu acho, não tenho certeza se isso muda o tempo de execução atual? Apesar de tudo, não há planos atuais para isso.

Parece que agora recebo um erro um pouco diferente do que antes ao tentar isso em um Node mais antigo, mas nenhum dos erros ficou claro de que a versão mínima do Node era o problema. É fácil adicionar uma verificação da versão mínima com uma mensagem de erro mais útil?

Sim, adicionarei uma mensagem de erro melhor junto com alguma documentação assim que a implementação se estabilizar um pouco. Visto que a implementação é protegida por um sinalizador experimental, não acho que alguém vá tropeçar nisso.

@SimenB , atualizei o Jest para 25.5.2 e agora todos os testes estão passando. Os parâmetros de consulta estão funcionando e os erros intermitentes que eu estava vendo antes não estão mais acontecendo. Obrigado novamente por todo o seu trabalho!

Ok, eu vi os erros novamente na minha última execução dos testes, então isso ainda está acontecendo. Vou ver se consigo descobrir uma maneira de reproduzir e relatar de forma consistente.

Acredito ter encontrado uma maneira consistente de reproduzir o problema. Aqui está o branch em que estou trabalhando: https://github.com/beejunk/firn.js/tree/switch-to-jest

Reproduzir:

  1. Exclua o cache do Jest se ele existir. Acabei de excluir manualmente /tmp/jest_rs .
  2. Execute npm test três vezes. As duas primeiras execuções devem ser bem-sucedidas. No entanto, a terceira execução deve falhar.

Este é o rastreamento de pilha que vejo quando o erro ocorre:

    Error: 
        at invariant (/home/brian/Projects/firn.js/node_modules/jest-runtime/build/index.js:1866:11)
        at Runtime.loadEsmModule (/home/brian/Projects/firn.js/node_modules/jest-runtime/build/index.js:480:7)
        at Runtime.linkModules (/home/brian/Projects/firn.js/node_modules/jest-runtime/build/index.js:548:19)
        at importModuleDynamicallyWrapper (internal/vm/module.js:397:21)
        at htmPreactPath (internal/process/esm_loader.js:31:14)
        at renderPage (/home/brian/Projects/firn.js/lib/renderPage.js:53:15)
        at map (/home/brian/Projects/firn.js/lib/build.js:43:12)
        at Array.map (<anonymous>)
        at build (/home/brian/Projects/firn.js/lib/build.js:36:43)
        at Object.<anonymous> (/home/brian/Projects/firn.js/test/build.test.js:57:5)

O erro parece ter origem nas importações dinâmicas. Não há mensagem de erro, então não tenho certeza do que está acontecendo.

Uma observação adicional: se eu limpar o cache do Jest e atualizar o script de teste adicionando --no-cache , não poderei reproduzir o problema.

Heh, fui preguiçoso e não enviei uma mensagem de erro adequada. O problema é que o ambiente de teste foi destruído, então estou supondo que está faltando await algum lugar. Não vi nada no seu código, então terei que pesquisar mais

@SimenB Aqui está uma reprodução desse problema de configuração do ESM: https://github.com/facebook/jest/issues/9935

@SimenB Eu criei um exemplo mínimo para reproduzir os erros do Jest que mencionei acima ao usar importações dinâmicas:

https://github.com/beejunk/jest-esm-dynamic-import-error

Obrigado pela ótima reprodução @beejunk! Passei mais horas que gostaria de admitir aqui, sem realmente entender se é um bug no Jest ou no Node. Eu reproduzi o comportamento usando apenas módulos de núcleo de nó e o relatei upstream, então vamos ver o que eles dizem: https://github.com/nodejs/node/issues/33216

Obrigado por investigar, @SimenB. Os testes parecem passar consistentemente se eu adicionar o sinalizador --no-cache , o que é bom para o meu caso de uso. Agradeço todo o trabalho!

Sim, também percebi isso. Acho que é algum tipo de problema de tempo - sem cache as coisas são lentas o suficiente para funcionar.

@SimenB Obrigado por resolver o # 9935. Mencionei uma segunda preocupação ali, que acho que ainda é válida. Quando type: "module" , jest --init ainda está gerando o arquivo de configuração com module.exports nele. Isso é relativamente pequeno para alterar manualmente se você sabe o que está fazendo, mas acho que se ESM não estiver sinalizado no Node e muitas pessoas começarem a fazer projetos ESM, ele começará a se parecer mais com um bug confuso (ou seja, caminho feliz de jest --init && jest em um novo projeto ESM causará um erro). Devo enviar um problema diferente especificamente sobre como melhorar a lógica do init?

@aldeed tem certeza? O teste agora me dá um arquivo mjs com export default nele. Acho que poderíamos gerar js e não mjs , mas ainda assim. Ele usa sintaxe ESM

@SimenB Bem, eu tinha certeza até você perguntar. “Eu tentei e você está correto. Talvez eu tenha feito isso inicialmente com uma versão mais antiga do Node or Jest? Desprezo.

Isso é incrível! Acabei de refazer os testes em uma de minhas bibliotecas para usar os Módulos ES e larguei o Babel. Obrigado @SimenB!

Detectar se um arquivo deve estar no modo ESM ou CJS

Falando nisso, há muitos pacotes orientados a navegador / empacotador usando a sintaxe "module":"<path to es module>" para denotar suas exportações de módulo ES. Pode ser prudente ter alguma maneira de especificar como resolver um determinado pacote, independentemente das configurações do próprio pacote. Algo como moduleNameMapper mas para especificar se é CJS ou ESM.

oi @SimenB , assim que este problema for ts-jest também pode cancelar commonjs certo? A extensão do arquivo é necessária para mudar do lado do transformador para funcionar com o ESM?

Por exemplo, agora ts-jest compila ts para js para commonjs , esm requer extensão de arquivo mjs ao compilar de ts a js ?

@zandaqo não suportamos o campo modules , estaremos seguindo as especificações do nó e usaremos exports : # 9771. Você pode conectar seu próprio resolvedor para oferecer suporte a modules se desejar: https://jestjs.io/docs/en/configuration#resolver -string. Podemos adicionar alguma outra opção ( mainFields , como webpack talvez?), Mas isso virá mais adiante quando a implementação se estabilizar e tivermos menos incógnitas desconhecidas 🙂


@ahnpnl # 9860

Ciao galera!
Só uma pergunta: a postagem do blog menciona que, como os módulos ES6 são estáticos, eles não podem ser simulados; então, na verdade, não há como simular um módulo A importado por um módulo B no ES6?

@gabrieledarrigo Eu uso moduleNameMapper para isso, por exemplo:

    "moduleNameMapper": {
      "moduleA": "<rootDir>/test/moduleA-mock.js"
    },

@gabrieledarrigo você pode fazer

jest.mock('the-thing-i-want-to-mock', () => /* do whatever in here */);

let importedThing;

beforeAll(async () => {
  importedThing = await import('thing-that-imports-mocked-module');
});

Portanto, você só precisa tornar a importação não estática e a simulação funcionará.

Não tenho certeza se funciona agora, pois ainda não conectei resoluções de simulação no caminho do código ESM. Farei isso em algum momento em breve. Mas _provavelmente_ será assim que documentaremos simulações de módulo para ESM nativo.

Conforme mencionado nos pots do blog, iremos documentar esses padrões em algum momento, só temos que descobri-los 🙂

Uma ideia que tivemos foi esperar pelo nível superior, então poderíamos fazer isso com um plugin babel.

@SimenB Em primeiro lugar, obrigado pelo excelente trabalho aqui :)

Na verdade, eu enfrento um problema quando gostaria de criar um ambiente personalizado estendido de jest-environment-node . Preciso importar minha implementação de servidor para lá, que está escrita como ESM. Mas parece que o ambiente deve ser definido como cjs .
Minha pergunta é: há uma opção para definir testEnvironment personalizado como esm para poder importar meu módulo de servidor? Obrigado por qualquer conselho.

@ kuka-radovan você poderia abrir uma solicitação de recurso separada para isso?

ATUALIZAÇÃO: este problema agora é rastreado em https://github.com/facebook/jest/issues/10025

@SimenB Obrigado pelo conselho jest.mock acima. Acontece que estou convertendo alguns arquivos que precisam disso hoje. Posso confirmar que seu exemplo funciona quando o módulo simulado é um pacote node_modules , mas não está funcionando para mim simular um módulo no mesmo projeto.

Aqui está um exemplo simples:

// main.js
import secondary from "./secondary.js";

export default function main() {
  return secondary();
}

// secondary.js
export default function secondary() {
  return true;
}

// test.js
import { jest } from "@jest/globals";

jest.mock("./secondary.js");

let main;
let secondary;
beforeAll(async () => {
  ({ default: main } = await import("./main.js"));
  ({ default: secondary } = await import("./secondary.js"));
});

test("works", () => {
  secondary.mockReturnValueOnce(false); // TypeError: Cannot read property 'mockReturnValueOnce' of undefined
  expect(main()).toBe(false);
});

O mesmo padrão funciona exatamente quando "./secondary.js" é um nome de pacote. (Acho que o pacote que experimentei exporta CommonJS, se isso importa.)

Jest 26.0.1 com Nó 12.16.3

Alguma ideia ou devo enviar uma edição totalmente separada?

EDITAR: transform: {} na configuração, então nada de Babel

EDIT 2: Isso também não funciona:

jest.mock("./secondary.js", () => ({
  default: jest.fn()
}));

Trabalho incrível nisso.

No entanto, a menos que eu esteja fazendo algo errado, ainda não parece ser possível usar import() em um arquivo de teste CJS.

Estou executando o Jest com node --experimental-vm-modules node_modules/jest/bin/jest.js e tenho testEnvironment: 'node', transform: {} em jest.config.js . Isso está no Nó 14.2.0.

A expressão de importação produz erro:

TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]:
A dynamic import callback was not specified.

Esta é uma limitação conhecida no momento? Vejo que https://github.com/nodejs/node/pull/32985 agora está no Nó 14.1.0.

Sim, ainda não comecei a implementá-lo. Provavelmente vou pousar neste fim de semana.

@aldeed você poderia abrir um problema separado? Preciso verificar se as simulações fazem parte da resolução e o seu exemplo parece um bom caso de teste 🙂

@SimenB Obrigado pela resposta rápida. Vou ficar atento para quando ele pousar.

Vendo como import em scripts pode ser revertido devido a uma regressão (https://github.com/nodejs/node/issues/33166), vamos esperar até que se resolva

Estou tendo problemas ao tentar usar isso com .mjs arquivos de teste. Se eu tiver __tests__/my-test.mjs , recebo

$ yarn test
yarn run v1.22.4
$ node --experimental-vm-modules node_modules/jest/bin/jest.js
No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In C:\Users\Domenic\Dropbox\Programming\WIP\remember-to-eat
  1 file checked.
  testMatch: **/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x) - 0 matches
  testPathIgnorePatterns: \\node_modules\\ - 1 match
  testRegex:  - 0 matches
Pattern:  - 0 matches
error Command failed with exit code 1.

Se eu adicionar

"testMatch": ["**/__tests__/**/*.mjs"]

para o meu package.json, eu recebo

$ yarn test
yarn run v1.22.4
$ node --experimental-vm-modules node_modules/jest/bin/jest.js
No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In C:\Users\Domenic\Dropbox\Programming\WIP\remember-to-eat
  1 file checked.
  testMatch: **/__tests__/**/*.mjs - 0 matches
  testPathIgnorePatterns: \\node_modules\\ - 1 match
  testRegex:  - 0 matches
Pattern:  - 0 matches
error Command failed with exit code 1.

No entanto, se eu remover "testMatch" e renomear meu arquivo para __tests__/my-test.js , ele funcionará.

Eu gostaria de poder usar consistentemente extensões .mjs em meu projeto. Isso é possível com Jest?

@domenic também encontrei isso. A solução é adicionar a configuração "moduleFileExtensions": ["js", "mjs"] (além de "testMatch" ).

Dei uma olhada e moduleFileExtensions é realmente necessário.

Jest obtém uma lista de todos os arquivos do projeto executando hasteFS.getAllFiles() aqui:

https://github.com/facebook/jest/blob/2460c059ad1dbf124466ac25c8d5ccfd74ae9f25/packages/jest-core/src/SearchSource.ts#L159 -L164

hasteFS é criado como parte de HasteMap com a seguinte configuração extensions :

https://github.com/facebook/jest/blob/2460c059ad1dbf124466ac25c8d5ccfd74ae9f25/packages/jest-runtime/src/index.ts#L291


No entanto, não acho que seja necessário especificar moduleFileExtensions neste caso. Já forçamos .snap a ser encontrado, devemos forçar extensões JS conhecidas também? Aqueles sendo (em cima da minha cabeça) js , mjs , cjs , jsx , ts e tsx ? Isso tornará o rastreamento mais lento, mas não acho que tenha um grande impacto. Posso estar errado, entretanto? Como padrão, não deve ser muito mais lento, pois apenas cjs e mjs já não fazem parte dos padrões padrão, mas para pessoas que têm padrões personalizados isso pode desacelerar as coisas?

Sim, seria ideal se, pelo menos no modo de módulos ES, .mjs funcionasse, sem ter que adicionar moduleFileExtensions ou modificar o testMatch padrão.

Também seria bom se eu pudesse excluir arquivos .js; quando eu tentei isso eu consegui

 Validation Error:

  moduleFileExtensions must include 'js':
  but instead received:
    ["mjs"]
  Please change your configuration to include 'js'.

Estou me perguntando se faz sentido ter algum "modo ESM" que adicionaria as extensões de arquivo esm do nó e também ajudaria na compilação para js usando esm para ativar (# 9860).


Sem js algumas coisas quebram internamente que carregamos dentro da sandbox (já que usa a mesma implementação require etc). Provavelmente devemos consertar isso para que o usuário não possa nos quebrar.

Em relação à desaceleração, já é bastante lento em grandes projetos, mas não sei se o número de extensões impacta tanto. Mas concordo que mjs e cjs devem ser adicionados como padrões. Especificar moduleFileExtensions: ['js'] substituiria os padrões e aceleraria, certo? Então, talvez apenas documente isso como um ajuste de desempenho.

Obrigado por todo esse trabalho! Certamente é incrível. Eu segui as 3 etapas ( "type": "module" no meu package.json, "testEnvironment": "jest-environment-node" na minha configuração de brincadeira e --experimental-vm-modules na CLI) e parece estar funcionando bem 🎉

Mas então estou tentando ler e usar import.meta conforme descrito na documentação do Node.js (e que parece já ter sido implementado a julgar pela caixa de seleção) para criar __dirname , mas parece que import.meta está falhando:

console.log(import.meta);

SyntaxError: [PATH]/files.test.js: Support for the experimental syntax 'importMeta' isn't currently enabled (31:20):
    Add @babel/plugin-syntax-import-meta (https://git.io/vbKK6) to the 'plugins' section of your Babel config to enable parsing.

Não tenho nenhuma babel e pensei que a babel estava ficando para trás com esse trabalho. Voltarei para relatar se posso consertar de alguma forma sem instalar o babel.

Node.js v14.3.0, Jest v25.5.4

Eu encontrei uma solução alternativa por enquanto. Como estou executando o script do mesmo diretório em que meu arquivo está em minha biblioteca , posso apenas fazer:

const __dirname = process.cwd();
const __filename = __dirname + "/files.test.js";

Seguirei este repositório caso haja alguma atualização, obrigado novamente por fazer isso!

Você precisa cancelar explicitamente o Babel usando transform: {} como configuração

@SimenB Posso confirmar que adicionar transform: {} funcionou, obrigado! Eu entendi mal esse ponto como "não adicione transformações" e não como "retire as transformações" como pretendido.

BTW, o teste foi de 2,4 segundos para apenas 1,3 segundos e eles parecem consistentemente mais rápidos.

O Nó 12 foi lançado com ESM não sinalizado (https://nodejs.org/en/blog/release/v12.17.0/). Porém, conforme observado no OP, o uso de APIs Jest _não_ não está marcado

@SimenB Eu

Ao executar o Jest 26.0.1, recebo este erro:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /app/tests/setup.js
require() of ES modules is not supported.
require() of /app/tests/setup.js from /app/node_modules/@jest/transform/build/ScriptTransformer.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename setup.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /app/package.json.

Tenho transform: {}, e executando node --experimental-vm-modules node_modules/jest/bin/jest.js .

o que estou perdendo?

@aldarund não tem certeza, você poderia fazer uma reprodução mínima?

@SimenB aqui está um repo mínimo para repro, apenas execute yarn test https://github.com/aledalgrande/jest-example - Eu tentei com o Nó 13/14 e é o mesmo resultado. Parece-me que o fluxo da configuração global não foi atualizado para funcionar com o ESM.

Além disso, você mencionou outra pessoa 😆

~ Não @simenB , mas @aledalgrande parece que tudo está correto aqui pelo que tentei, veja meu projeto totalmente rodando no ESM para comparação (configuração de jest em package.json ). ~

~ Para depurar, se possível, sugiro simplificar sua configuração de brincadeira para _apenas_ ter as duas propriedades relevantes, talvez até mesmo no package.json primeiro. Em seguida, adicione cada uma das outras propriedades que você tem atualmente para ver qual funciona / não funcionou. ~

Ah o segundo comentário menciona globalSetup e não testes normais, nvm meu comentário então. Se eu remover a chave globalSetup no Jest, o teste será executado conforme o esperado naquele exemplo, mas a chave globalSetup não funciona como você disse.

Aha, esqueci-me da configuração e desmontagem global. Pode consertar 👍

Olá @SimenB , eu de novo. As exportações nomeadas são suportadas? Com o Node.js, posso importar e usar um pacote como este:

import { customAlphabet } from "nanoid";

No entanto, ao tentar fazer um teste, o mesmo código dá este erro:

SyntaxError: The requested module 'nanoid' does not provide an export named 'customAlphabet'

Para os testes, posso alterar o código para este e funciona:

import nanoid from "nanoid";
const { customAlphabet } = nanoid;

Mas então a versão Node.js para de funcionar, pois não há esporte padrão (mas por algum motivo a exportação padrão funciona com Jest):

SyntaxError: The requested module 'nanoid' does not provide an export named 'default'

O código publicado (o repo parece estar em fluxo agora) nanoid termina assim, sem exportação padrão:

export { nanoid, customAlphabet, customRandom, urlAlphabet, random }

Jest consome apenas o ponto de entrada "principal". as "exportações" ainda não são consideradas. Você acabou de importar a versão commonjs que tem apenas exportação padrão.

Ah, entendo, o package.json parece incluir isto:

  "main": "index.cjs",
  "module": "index.js",
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "require": "./index.cjs",
      "import": "./index.js",
      "browser": "./index.browser.js"
    },
    ...
  }
  ...

Portanto, provavelmente Node.js está encontrando a versão do módulo, enquanto Jest está usando a versão CommonJS que não tem uma exportação nomeada, certo?

Vou esperar até que Package Exports seja verificado e depois testarei, obrigado por todo o trabalho novamente! Marcando esses 2 comentários como resolvidos até então. O teste a que me refiro é este .

Estou revisitando isso para ver como está funcionando - atualizado para Jest 26.0.1 e nó 14.4. Defina package.json como o tipo de módulo, defina transform como {} , env como jest-environment-node e execute node --experimental-vm-modules . Agora recebo este novo erro:

ES Modules are only supported if your test environment has the `getVmContext` function

Não consegui encontrar informações sobre isso, exceto um changelog de Jest dizendo que getVmContext tinha sido adicionado um tempo atrás.

Alguma ideia?

Você poderia compartilhar as partes relevantes do seu package.json please @cyberwombat ? Incluindo o script de lançamento que você está usando para Jest.

Para referência, é assim que parece para mim em um projeto de trabalho :

{
  ...
  "type": "module",
  "scripts": {
    ...
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
  },
  "jest": {
    "transform": {},
    "testEnvironment": "jest-environment-node"
  },
  ...

Em seguida, inicie-o com npm test

@franciscop O meu basicamente é o mesmo. Nó 14.4.0. Eu posso administrar o seu bem. Vou mergulhar nas coisas para ver o diff.
package.json

{
  "type": "module",
  "devDependencies": {
    "jest": "^26.0.1",
  },
}

jest.config.js

export default {
  testEnvironment: 'jest-environment-node',
  setupFilesAfterEnv: ['./test/bootstrap.js'],
  testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/config/', '/<rootDir>/src/'],
  testRegex: '(\\.|/)(test|spec)\\.[jt]sx?$',
  transform: {
//    '^.+\\.jsx?$': 'babel-jest' // esm someday
  },
  transformIgnorePatterns: [],
  modulePaths: [
    '<rootDir>/test',
    '<rootDir>/src',
    '<rootDir>'
  ]
}

Roteiro:
node --experimental-vm-modules node_modules/jest/bin/jest.js

Não tenho certeza, mas tentaria fazer o contrário. Remova tudo, exceto transform: {} e testEnvironment: 'jest-environment-node' , e comece a adicionar cada uma das opções até ver qual delas dispara o erro anterior. Suspeito especialmente que transformIgnorePatterns _pode_ estar em conflito com transform , mas não estou familiarizado com as opções de jest.

Olá pessoal! Tive alguns problemas ao usar o Jest para testar um aplicativo Express. Mais detalhes aqui . Não tenho certeza se isso é útil para o que você está fazendo / rastreando aqui: roll_eyes:

@ x80486 Encontrei exatamente o mesmo problema ontem . Eu respondi no StackOverflow com uma explicação mais longa do meu entendimento.

Edit: Eu mostrei meu comentário anterior, pois parece que pode ser relevante, este "exports" parece ser popular, muito provavelmente deste artigo sobre pacotes híbridos .

exports é rastreado em # 9771

@franciscop ok problema resolvido - acontece que há um conflito nos pacotes - eu tinha serverless-bundle instalado que causava o erro ES Modules are only supported if your test environment has the getVmContext function . Não tenho certeza do porquê - eu presumiria que instalá-lo não causaria um conflito de execução, mas evidentemente causa.

@franciscop Acho que a razão pela qual pkg.exports problemas relacionados comecem a surgir agora é porque esse recurso não estava sinalizado no Node.js 14.x e alguns mantenedores de pacotes (como eu por uuid ) começaram adicionando campos pkg.exports . Portanto, embora você precisasse de um sinalizador de linha de comando para ativar esse recurso no Node.js 12.x você obtém esse comportamento por padrão agora.

Vai demorar um pouco para que todo o ecossistema se adapte, então, obrigado por relatar problemas em torno desse tópico!

Para aqueles que postam sobre exports , caso tenha se perdido no longo tópico deste problema, meu problema encerrado sobre ele (https://github.com/facebook/jest/issues/9565) tem um exemplo da solução alternativa moduleNameMapper nele.

O problema globalSetup relatado em maio provavelmente ainda está lá (Jest 26.1.0)? Obtendo os mesmos erros que no exemplo repo @aledalgrande fornece:

$ git clone [email protected]:aledalgrande/jest-example.git
$ cd jest-example
$ npm test

> @ test /Users/asko/Temp/jest-example
> node --experimental-vm-modules node_modules/jest/bin/jest.js --config=./jest.config.js

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/asko/Temp/jest-example/tests/setup.js
require() of ES modules is not supported.
require() of /Users/asko/Temp/jest-example/tests/setup.js from /Users/asko/Temp/jest-example/node_modules/@jest/transform/build/ScriptTransformer.js 

Sem pressa. Verificado CHANGELOG e não mencionou uma correção para globalSetup / globalTeardown com ES6.

Node.js 14.4.0, Jest 26.1.0


Atualização (13 de agosto de 20):

Ainda não é possível, Node.js 14.7.0, Jest 26.4.0

Opinião lateral, mas esse problema deveria ser um problema fixo, já que é o foco de brincadeira no momento?

Alguma ideia do que precisa ser feito para consumir repórteres de teste escritos em módulos ES? ...
com a versão mais recente do jest, estou recebendo um erro que diz essencialmente que o testScheduler espera um repórter personalizado no formato commonjs.


para ver o erro

~ / projects / esw-ts / lib / dist / test / testReporter.js: 1
importar os de 'os';
^^^^^^

SyntaxError: Não é possível usar a instrução de importação fora de um módulo
em wrapSafe (internal / modules / cjs / loader.js: 1116: 16)
em Module._compile (internal / modules / cjs / loader.js: 1164: 27)
em Object.Module._extensions..js (internal / modules / cjs / loader.js: 1220: 10)
em Module.load (internal / modules / cjs / loader.js: 1049: 32)
em Function.Module._load (internal / modules / cjs / loader.js: 937: 14)
em Module.require (internal / modules / cjs / loader.js: 1089: 19)
em require (internal / modules / cjs / helpers.js: 73: 18)
em /Users/manish.gowardipe/Desktop/projects/esw-ts/lib/node_modules/@jest/core/build/TestScheduler.js:418:65
em Array.forEach ()
em TestScheduler._addCustomReporters (/Users/manish.gowardipe/Desktop/projects/esw-ts/lib/node_modules/@jest/core/build/TestScheduler.js:411:15)

Olá, quero testar o suporte nativo para módulos ES em meu pequeno projeto, mas sou novo no NodeJS e me perdi nesta edição. Adoraria alguma orientação, por favor.

  • node --version : v14.5.0
  • yarn jest --version : 26.1.0
  • Estou tentando testar esse pequeno projeto , é muito simples.
  • Eu tenho meus arquivos assim:

package.json

{
"jest": {
    "transform": {},
    "testEnvironment": "jest-environment-node"
  }
}

markov.test.js

const fs = require("fs");
const Markov = require("./markov.mjs");
// import fs from "fs";
// import Markov from "./markov.mjs";

const file = fs.readFileSync("text.txt", "utf8");
const markov = new Markov(file.toString());

test("Generates sentence with especified words", () => {
  expect(markov.makeSentence(8).length).toBe(8);
});
  • Eu corro yarn jest . e isso me dá este erro:
    imagen

  • Tentei com node node_modules/jest/bin/jest.js . e deu o mesmo erro.

@ pepetorres1998 Este tópico é sobre a execução de Jest com módulos esm nativos que envolvem a execução de coisas com certas sinalizações / opções - veja o comentário acima para saber o que fazer (e definir "type": "module" em package.json). Honestamente, porém, neste momento não está totalmente pronto para o horário nobre, então se você está precisando que seu projeto funcione, posso ficar com Babel. Há uma série de problemas não verificados que realmente impedem o show. Eu tentei mudar alegremente algumas semanas atrás e voltei chorando para Babel.

Alguém está recebendo um ReferenceError: jest is not defined ao tentar fazer coisas como jest.setTimeout(...) em um arquivo de teste com esta configuração? Tentar descobrir se isso está relacionado ao ambiente do módulo, versão do nó, versão da brincadeira ou alguma combinação dessas coisas. (Atualmente usando o nó v14.5.0, jest 26.1.0, ambiente jest-ambiente-nó)

EDITAR
Agora vejo a caixa de seleção desmarcada na descrição do problema para a propriedade 'global' do jest. 🙃

@bdentino , acho que você pode tentar importá-lo explicitamente import {jest} from '@jest/globals';

25.4.0 foi lançado com as primeiras peças de suporte. Além do # 9772 mencionado acima, também incluí o # 9842. Na _teoria_, a mistura de CJS e ESM deve funcionar corretamente agora (🤞).

O único recurso que falta é o suporte ao objeto jest . Ainda não decidi se devemos mantê-lo em import.meta ou exigir que as pessoas o importem através de import {jest} from '@jest/globals' . Agradecemos o feedback!

Eu não escrevi documentos para isso ainda, mas para ativá-lo, você precisa fazer três coisas

  1. certifique-se de não executar as instruções transform import (defina transform: {} na configuração ou então certifique-se de que babel não transforme o arquivo em CJS, evitando o modules opção para predefinir o env)
  2. Execute node@^12.16.0 || >=13.2.0 com a bandeira --experimental-vm-modules
  3. Execute seu teste com jest-environment-node ou jest-environment-jsdom-sixteen

Experimente e forneça comentários! Se relatar bugs, seria maravilhoso se você também pudesse incluir como a execução do mesmo código (sem qualquer código específico de teste) é executado no Node. Eu li https://nodejs.org/api/esm.html _muito_ nas últimas semanas, mas provavelmente deixei passar algo.

@SimenB
Este tópico se tornou enorme, e eu acho que aqueles que querem começar com módulos ES de brincadeira / uso - terão dificuldade em encontrar e entender as diretrizes básicas para começar a fazê-lo.
Existe uma explicação formal nos documentos sobre como adicionar jest a um projeto de módulos ES (ou algum 'início rápido')?

@aldeed Com relação ao seu problema com módulos de simulação do mesmo projeto, você encontrou uma solução? Estou tendo exatamente o mesmo problema

(Aliás, nós também usamos o responsecommerce, então brinde a isso haha)

@guilhermetelles não, e é rastreado em https://github.com/facebook/jest/issues/10025 agora.

Estou usando o Jest 26.1.0, node versão 14.6.0 com --experimental-vm-modules , mas ainda estou vendo ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING ao usar import() dentro do CommonJS . Devo tentar criar uma reprodução mínima e abrir um novo problema?

Como um aparte, existe uma maneira fácil de yarn link uma cópia de jest pacotes em um projeto agora que Jest usa yarn berry? Eu queria experimentar o master mais recente apenas no caso de isso ter sido implementado por ainda não lançado. Eu estava tentando fazer algo como path/to/facebook/jest/.yarn/releases/yarn-sources.cjs link --all path/to/jest , mas não deu certo. Executar manualmente algo como cd node_modules; for p in jest*; do if [[ -d path/to/jest/packages/$p ]]; then rm -rf $p; ln -s path/to/jest/packages/$p; fi; done também não estava funcionando, não sei por quê.

@vvanpo import() no CJS foi revertido no Node, você pode seguir https://github.com/nodejs/node/issues/31860

Quanto à execução local, geralmente desinstalo jest do projeto que quero testar e faço ../jest/jest . Potencialmente nose ../jest/packages/jest/bin/jest.js . Apenas certifique-se de executar yarn e yarn build:js primeiro. Se essas instruções não funcionarem (estou escrevendo de memória em um telefone em um avião), abra um problema (ou PR) para que possamos escrever corretamente no arquivo CONTRIBUTING.md

Você planeja oferecer suporte a importações cíclicas?

Se eu tiver um arquivo de teste fictício que importa apenas um dos dois arquivos que importam apenas um ao outro, obtenho RangeError: Maximum call stack size exceeded . Se eu remover uma das importações, o teste será aprovado. Repo que reproduz o problema .

Ei! Eu configurei isso em um projeto de nó vazio e funcionou muito bem, no entanto, em nossa configuração de produção, recebo a seguinte mensagem de erro quando estou tentando executar testes:

ES Modules are only supported if your test environment has the 'getVmContext' function

Eu vi outra pessoa tendo algum problema em uma resposta anterior (por @cyberwombat ), mas o pacote que eles descobriram ser o culpado não está presente em nosso arquivo package.json . Como deduzir o pacote (ou configuração) que causa o problema? Tentei remover sistematicamente todas as configurações de gracejo desnecessárias para fazer este trabalho, mas não tive sucesso.

ATUALIZAÇÃO : consegui progredir fazendo uma pequena alteração em jest-runtime . Parei o depurador na linha que tenta acessar o contexto da VM e, embora a função realmente não exista, this.context (que deve retornar) existe, então mudei essa linha para acessar a propriedade diretamente. Eu sei que provavelmente não é o ideal, mas talvez @SimenB isso possa lhe dar uma ideia do que está errado.

Obrigado desde já por qualquer ajuda

Você planeja oferecer suporte a importações cíclicas?

Com certeza! Você poderia abrir um problema separado?


@zsombro parece que você está executando uma versão antiga do ambiente de teste. Se você executar jest --show-config , o que é exibido por testEnvironment ?

parece que você está executando uma versão antiga do ambiente de teste. Se você executar jest --show-config , o que é exibido por testEnvironment ?

@SimenB diz o seguinte:

"testEnvironment": "/Users/zberki/git/project-name/node_modules/jest-environment-node/build/index.js",
"testEnvironmentOptions": {},

Acabei de definir como jest-environment-node base nas suas instruções

Antes de iniciar este processo, atualizei o jest usando yarn add jest@latest . Devo atualizar o ambiente separadamente?

ATUALIZAÇÃO: Acontece que eu tinha que fazer. Excluí node_modules e yarn.lock para fazer uma instalação limpa e ainda não funcionou. No entanto, se eu adicionar manualmente usando yarn add -D jest-environment-node , parece funcionar. Existe uma maneira melhor de gerenciar isso? Fiz um projeto de teste minimalista antes de fazer isso em nossa base de código e não tive que fazer isso

yarn list jest-environemnt-node (ou npm list jest-environemnt-node ) provavelmente listará vários, é meu palpite

├─ [email protected]
│  └─ [email protected]
└─ [email protected]

a versão 26.2.0 é provavelmente a que instalei manualmente (pelo menos com base em package.json , o que significa que jest-config instalou uma versão aparentemente desatualizada?

Você tem outra coisa puxando uma versão mais antiga de jest-config ( react-scripts talvez (parte de create-react-app )?). Este assunto não é o lugar para discuti-lo, embora 🙂

Não poder usar módulos ES em meu globalSetup está começando a doer.

Dois pontos:

  • isso deve ser mencionado como uma caixa de seleção no início deste problema (para que seja rastreado)?
  • se houver um alfa / beta eu poderia tentar, disposto a fazê-lo

EU:

  • Garanti que estou executando a versão mais recente do Jest (26.4.0)
  • Adicionado jest-environment-node ao meu projeto
  • Garantido que não seja duplicado inspecionando o arquivo de bloqueio
  • Adicionado "testEnvironment": "jest-environment-node", em jest.config.json
  • Adicionado import { jest } from '@jest/globals'; sempre que o jest foi usado
  • Executou a configuração do comando de teste --experimental-vm-modules executando-os com NODE_OPTIONS='--experimental-vm-modules' yarn jest

E ele trava no seguinte código:

jest.mock('../../some/other/path', () => ({
  someOtherMethod: jest.fn().mockImplementation(…),
}));

com o seguinte erro (abreviado - nota "Objetos permitidos"!):

ReferenceError: src/foo/bar.spec.js: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
Invalid variable access: jest
Allowed objects: Array, …, jest, …, unescape.
Note: This is a precaution to guard against uninitialized mock variables. If it is ensured that the mock is required lazily, variable names prefixed with `mock` (case insensitive) are permitted.

Não posso usar o Babel porque ele analisa incorretamente as importações que corrigi para executar no Nó 14 sem Babel:

-import { map } from 'lodash';
+import lodash from 'lodash';
+const { map } = lodash;

Que, infelizmente, é analisado incorretamente por @babel/preset-env , resultando em TypeError: Cannot destructure property 'map' of '_lodash.default' as it is undefined. .

Alguém pode me ajudar a solucionar esse problema?

Edit: Parece que você _pode_ usar Jest + Babel em código compatível com módulos ES nativos usando importações CommonJS fazendo esta correção absolutamente nojenta:

jest.mock('common-js-module', () => ({
  __esModule: false,
  ...jest.requireActual('common-js-module'),
}));

Por aqui,

import lodash from 'lodash';
const { map } = lodash;

é perfeitamente consumido pelo Nó 14, e o código resultante da execução de Jest + Babel,

var _lodash = _interopRequireDefault(require("lodash"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const {
  map
} = _lodash.default;

também funciona.

convertemos com sucesso todos os nossos testes de jest para usar e importar nosso código ES6, mas ficamos presos em alguns pacotes: a saber puppeteer e uuid

O aplicativo só funciona se nós os importarmos para um objeto (como import uuid from 'uuid' ), mas os testes não serão executados dessa forma. No entanto, se substituirmos essa importação pela sintaxe de desconstrução (como import { v4 } from 'uuid' , será o contrário: o teste funciona, mas o aplicativo lança uma exceção.

originalmente, seguimos o guia e desligamos todas as transformações, mas também tentamos criar um espaço de trabalho de fios onde instalamos o babel com uma configuração de nó mínima, mas isso não resolveu (ou piorou) este problema específico

No entanto, se substituirmos essa importação pela sintaxe de desconstrução (como import {v4} de 'uuid', será o contrário: o teste funciona, mas o aplicativo lança uma exceção.

Parece que seu aplicativo foi compilado para CommonJS e não está usando módulos na prática. Do ESM "real" import uuid from 'uuid' não deve funcionar porque uuid não tem exportação padrão e expõe uma construção ESM para o nó .

Olá @SimenB , você acha que alguma documentação preliminar sobre isso seria uma boa ideia?

@grantcarthew definitivamente! Eu esperava poder gastar mais tempo nisso e estabilizá-lo para Jest 27, mas duvido que consiga fazer isso. Mas escrever uma página de documento sobre o que está lá agora (e que é experimental) parece uma boa ideia

@SimenB Não sei qual é o estado atual do problema e se Jest já deve trabalhar com meu caso ou não, mas talvez possa ajudá-lo de alguma forma.

Estou tentando carregar uma biblioteca somente ESM (sua extensão é cjs, mas o tipo é módulo e o nó parece estar ok com isso), mas Jest não consegue carregá-la corretamente com o erro:

    C:\dev\codemirror-next-repro-cra\test-in-jest-esm\node_modules\style-mod\dist\style-mod.cjs:15
    export var StyleModule = function StyleModule(spec, options) {

Aqui, o problema que abri originalmente https://github.com/codemirror/codemirror.next/issues/310. E uma reprodução para Jest + ESM falhando com o nó 14.13.1 https://github.com/dubzzz/codemirror-next-repro-cra/tree/main/test-in-jest-esm

@dubzzz você não pode ter ESM em um arquivo cjs . O nó também falha

$ node node_modules/style-mod/dist/style-mod.cjs
(node:48829) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/simen/repos/codemirror-next-repro-cra/test-in-jest-esm/node_modules/style-mod/dist/style-mod.cjs:15
export var StyleModule = function StyleModule(spec, options) {
^^^^^^

SyntaxError: Unexpected token 'export'
    at wrapSafe (internal/modules/cjs/loader.js:1172:16)
    at Module._compile (internal/modules/cjs/loader.js:1220:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1277:10)
    at Module.load (internal/modules/cjs/loader.js:1105:32)
    at Function.Module._load (internal/modules/cjs/loader.js:967:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47

Oups, desculpe, tentei muito rapidamente no lado do nó. @ nicolo-ribaudo já notificou o autor da lib sobre este problema.
Muito obrigado pela sua resposta rápida.

Abri um PR para alguns documentos (praticamente stub) aqui: # 10611. Não me incomodei em enumerar os recursos / bugs ausentes, pois acho que será difícil manter a sincronia com a realidade, e olhar para os problemas do github é uma abordagem melhor, visto que estão (com sorte ...) atualizados.

@Pomax como um novo problema, por favor 🙂

Acabei de abrir o # 10620, que adiciona suporte para import() da CJS. Solicitados algumas vezes são como https://github.com/facebook/jest/issues/9430#issuecomment -626054595

Olá. É muito difícil para mim abraçar rapidamente toda a história por trás do ESM em node / jest, então, provavelmente, estou perguntando algo óbvio ou já respondido. Eu entendi corretamente que o caso a seguir ainda não é compatível? Ou, espero, estou fazendo algo que não está da maneira correta? Percebi que import x from 'x' funciona, mas a desestruturação de import { sub } from 'x' não.

package.json:

{
  "name": "jest-uuid",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "test": "node --experimental-vm-modules node_modules/.bin/jest"
  },
  "devDependencies": {
    "jest": "26.5.2"
  },
  "dependencies": {
    "uuid": "8.3.1"
  }
}

f.spec.js

import { v4 } from 'uuid';
test('', () => {});

teste npm

> npm test

> [email protected] test /Users/igoro/p/tmp/jest-uuid
> node --experimental-vm-modules node_modules/.bin/jest

 FAIL  ./f.spec.js
  ● Test suite failed to run

    SyntaxError: The requested module 'uuid' does not provide an export named 'v4'

      at jasmine2 (node_modules/jest-jasmine2/build/index.js:228:5)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.879 s
Ran all test suites.
(node:94492) ExperimentalWarning: VM Modules is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
npm ERR! Test failed.  See above for more details.

Você está esperando pelo # 9771. Antes disso, Jest não sabe que é seguro carregar uuid como ESM (ou melhor, qual arquivo carregar em qual ponto ele saberia que é ESM)

Isso seguirá a convenção do próprio Node, em que CJS só pode ser carregado como namespace, ou isso "melhorará" permitindo uma sintaxe que não funciona no próprio Node? (por exemplo, o Node não permite import { readdirSync } from "fs-extra" porque é um pacote CJS, mas permite import fs from "fs-extra"; que pode então ser descompactado usando const { readdirSync } = fs ).

(por exemplo, o Node não permite importar {spawn} de "child_process" porque é um pacote CJS, mas permite importar child_process de "child_process"; que pode então ser descompactado usando const {spawn} = child_process;).

Este é um exemplo infeliz porque o nó considera "child_process" como um módulo "embutido" (e não CJS), então as exportações nomeadas funcionam. O nodejs mais recente também usa uma heurística para fazer muitas exportações nomeadas funcionarem para módulos CJS. Essa pode ser a parte mais difícil de imitar.

exemplo atualizado para usar fs-extra lugar. Mas se a exportação nomeada está no roteiro do Node para pousar neste ou no próximo importante, então Jest antecipar isso faz sentido.

Isso já deve estar implementado - os módulos principais do nó expõem as exportações nomeadas, o CJS "normal" não.

O nodejs mais recente também usa uma heurística para fazer muitas exportações nomeadas funcionarem para módulos CJS. Essa pode ser a parte mais difícil de imitar.

Você tem um link para o PR que o implementa? Devemos tentar imitá-lo pelo menos 🙂

O PR está aqui: https://github.com/nodejs/node/pull/35249

A heurística por trás disso é publicada como cjs-module-lexer (https://github.com/guybedford/cjs-module Budaper ), mas

Acabei de dar uma olhada e parece que fs-extra está usando um padrão de exportação como:

module.exports = {
  // Export promiseified graceful-fs:
  ...require('./fs'),
  // Export extra methods:
  ...require('./copy-sync'),
  ...require('./copy'),
  ...require('./empty'),
  ...require('./ensure'),
  ...require('./json'),
  ...require('./mkdirs'),
  ...require('./move-sync'),
  ...require('./move'),
  ...require('./output'),
  ...require('./path-exists'),
  ...require('./remove')
}

Este não é atualmente um caso de análise de reexportação que detectamos, mas pode ser possível adicionar a cjs-module-lexer se esse for um caso útil para lidar com a detecção de exportações nomeadas.

Obrigado @jkrems & @guybedford! Eu abri um PR agora usando esse módulo: # 10673

O suporte fs-extra exato descrito em https://github.com/facebook/jest/issues/9430#issuecomment -713204282 agora está implementado em cjs-module [email protected] , rastreamento upstream em https: // github. com / nodejs / node / pull / 35745.

_Update: testando esta compilação, ele detecta corretamente todas as funções fs-extra, mas infelizmente não detecta as funções nativas do Node.js, pois elas não são estaticamente analisáveis ​​devido a serem preenchidas por um loop for._

talento: suporta exportações nomeadas de CJS como importações ESM # 10673

Achei que o ESM nativo só oferecesse suporte à importação de exports um módulo CommonJS como default ?

Olá. É muito difícil para mim abraçar rapidamente toda a história por trás do ESM em node / jest, então, provavelmente, estou perguntando algo óbvio ou já respondido. Eu entendi corretamente que o caso a seguir ainda não é compatível? Ou, espero, estou fazendo algo que não está da maneira correta? Percebi que import x from 'x' funciona, mas a desestruturação de import { sub } from 'x' não.

...
importar {v4} de 'uuid';

Os módulos ESM não oferecem suporte a importações de desestruturação, embora a sintaxe seja semelhante a isso. Para que isso funcione, 'export v4' é necessário. 'export default' não corresponderá.

https://kentcdodds.com/blog/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution

@sdwlig uuid fornece exportações nomeadas e não possui uma padrão. Deve funcionar, mas o carregamento do esm de pacotes com o campo "exportações" ainda não é suportado pelo jest. Em vez disso, o Commonjs é carregado, o que só está disponível por meio da exportação padrão.
https://github.com/uuidjs/uuid/blob/master/src/index.js

Podemos adicionar suporte de auto-referência de pacote (# 10883) a isso?

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