Cucumber-js: دعم وحدة JS الأصلية في تعريفات الخطوة ورمز الدعم

تم إنشاؤها على ٣ أبريل ٢٠٢٠  ·  13تعليقات  ·  مصدر: cucumber/cucumber-js

مشكلة

عندما أحاول تشغيل Cucumber.js بتعريف خطوة محدد في وحدة ECMAScript أصلية (كما هو موثق هنا ) ، فشلت المحاولة مع تحذير بأنني أستخدم CommonJS require() على وحدة ES.

إصدار Cucumber.js: 6.0.5.2
إصدار العقدة: 13.8.0

خطوات التكاثر

  1. قم بإعداد دليل حزمة NPM أساسي باستخدام npm init و npm i cucumber

    • قم بتعيين "type": "module" في ملف package.json للتأكد من أن ملفات JS ستعامل كوحدات نمطية أصلية

  2. أنشئ ملف ميزة أساسية ، features/mjs.feature :

    Feature: Native JS Modules
    
        Scenario: Load a native JS module step definition
            Given I have 42 cucumbers in my belly
    
  3. إنشاء ملف تعريف الخطوة الأساسية ، features/step_definitions.js :

    import { Given } from "cucumber";
    
    Given("I have {int} cucumbers in my belly", function (cucumberCount) {
        console.log("Step parsed.");
    });
    
  4. محاولة تشغيل Cucumber:

    $ ./node_modules/.bin/cucumber-js
    

نتيجة متوقعة

يجب تحميل وحدة تعريف الخطوة واستخدامها لتحليل الخطوة.

نتيجة فعلية

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /some/path/cucumbertest/features/step_definitions.js
require() of ES modules is not supported.
require() of /some/path/cucumbertest/features/step_definitions.js from /some/path/cucumbertest/node_modules/cucumber/lib/cli/index.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 step_definitions.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /some/path/cucumbertest/package.json.

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:13)
    at Module.load (internal/modules/cjs/loader.js:1000:32)
    at Function.Module._load (internal/modules/cjs/loader.js:899:14)
    at Module.require (internal/modules/cjs/loader.js:1040:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at /some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js:119:42
    at Array.forEach (<anonymous>)
    at Cli.getSupportCodeLibrary (/some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js:119:22)
    at Cli.run (/some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js:141:37)
    at async Object.run [as default] (/some/path/cucumbertest/node_modules/cucumber/lib/cli/run.js:30:14)

إغلاق

لما يستحق ، أنا أقدر حقًا العمل الذي قام به فريق Cucumber.js في هذا المشروع ، فقد كان أحد الأصول الرئيسية لي ولشركتي. لقد استثمر فريقي بعض الوقت في بناء بعض مكونات التطبيق المشتركة في وحدات JS الأصلية ، وحتى وقت قريب كنت أفترض (استنادًا إلى بناء الجملة import في أحدث الوثائق) أن هذه الأشياء ستعمل مع Cucumber عندما الالتفاف حول التكامل مع إطار الاختبار الخاص بنا. يبدو أن هذا ليس هو الحال ، على الرغم من ذلك. نظرًا لأن المكون المشترك مكتوب بالفعل ومتكامل في أجزاء أخرى من تطبيقنا ، فأنا متردد في استخدام تعريفات الخطوة فقط ورمز الدعم المكتوب في CommonJS بسبب تحديات التشغيل البيني.

تم تضمين أشياء أخرى حاولت الحصول عليها للعمل ...

  • إعطاء ملفات الوحدة الأصلية .mjs امتدادات ، لكل اصطلاح Node.js (تسبب هذا في تجاهلها من خلال منطق التحميل التلقائي لـ Cucumber ، واستخدام --require لتحميل وحدة تعريف الخطوة بشكل صريح ، رمى Error [ERR_REQUIRE_ESM]: Must use import to load ES Module
  • التفاف الوحدات الأصلية في وحدات CJS التي تستخدم الوظيفة الديناميكية import() ؛ كانت المشكلة أن هذا النهج غير متزامن ، وحتى إذا تم تصدير وعد الاستيراد بواسطة وحدة CJS ، لا يبدو أن الخيار ينتظر حل هذا الوعد قبل المتابعة ، لأنه تم وضع علامة على الخطوة على أنها غير محددة (مما يشير إلى أن تعريف الخطوة في الوحدة الأصلية لم يتم تسجيلها بعد).

إذا كان ذلك ممكنًا ، فسأكون سعيدًا بتقديم العلاقات العامة للمساعدة في حل هذه المشكلة ، لكنني لست على دراية بالأعمال الداخلية لـ Cucumber.js وسأكون ممتنًا لو استطاع أحد أن يوجهني في الاتجاه الصحيح.

accepted enhancement

التعليق الأكثر فائدة

أستخدم الامتداد .js و "type":"module" لأنني أتوقع أن يصبح هذا هو القاعدة على المدى الطويل.

ال 13 كومينتر

أود الحصول على cucumber-js يعمل مع ESM. كان لدينا بعض القضايا الأخرى التي تم فتحها حول هذا الموضوع بالفعل.

يمكننا إنشاء خيار CLI (أو استخدام طريقة أخرى للبحث عن وقت استخدامه) التي تجعل cucumber-js تستخدم import بدلاً من الطلب وانتظر الوعد المرتجع.

بالنظر إلى المثال الذي أضفته ، يمكننا إضافة حالة اختبار خصيصًا لهذا والعمل على اجتيازها. تتطلب تجربة التبديل الاستيراد وانتظار الوعد ، ثم واجهت خطأ آخر:

⋊> ~/p/test ./node_modules/.bin/cucumber-js                                21:05:22
(node:7422) ExperimentalWarning: The ESM module loader is experimental.
file:///test/features/steps.js:1
import { Given } from "cucumber";
         ^^^^^
SyntaxError: The requested module 'cucumber' does not provide an export named 'Given'
    at ModuleJob._instantiate (internal/modules/esm/module_job.js:92:21)
    at async ModuleJob.run (internal/modules/esm/module_job.js:107:20)
    at async Loader.import (internal/modules/esm/loader.js:176:24)
    at async Promise.all (index 0)
    at async Cli.getSupportCodeLibrary (/test/node_modules/cucumber/lib/cli/index.js:119:5)
    at async Cli.run (/test/node_modules/cucumber/lib/cli/index.js:141:32)
    at async Object.run [as default] (/test/node_modules/cucumber/lib/cli/run.js:30:14)

أخذت المثال (تم تعديله قليلاً) من وثائق تعريف الخطوة ، لكنك على حق ، فلن يعمل كما هو مكتوب. نظرًا لأن Cucumber.js عبارة عن وحدة نمطية CommonJS ، فإنه يوفر فقط تصديرًا افتراضيًا. على المدى القصير ، من المحتمل أن أقوم فقط باستيراد الوحدة الافتراضية ثم الوصول إلى الطرق المعطاة / متى / ثم من ذلك:

import cucumber from "cucumber";

cucumber.Given(...);

على المدى الطويل ، سيكون من الجيد أن يكون لدى الخيار نقاط دخول منفصلة للاستيراد والمطالبة ، كما هو موضح هنا .

شكرا لاطلاعك على هذا. بالنسبة لما يستحق ، فإن خيار CLI لإخبار Cucumber بالاستيراد بدلاً من الطلب يناسبني بشكل جيد. سيكون من الرائع أيضًا أن يبحث Cucumber عن ملفات دعم بامتدادات .cjs و .mjs ، ويعاملها تلقائيًا كوحدات CommonJS ووحدات نمطية أصلية على التوالي.

تحرير: أريد فقط أن أكرر أنني سعيد للمساعدة في أي من هذا إذا كان بإمكاني ؛ فقط وجهني في الاتجاه الصحيح.

من المحتمل أن أقوم فقط باستيراد الوحدة الافتراضية ثم الوصول إلى الطرق المعطاة / متى / ثم من ذلك:
استيراد الخيار من "الخيار" ؛
الخيار معطى (...) ؛

هذا لا يعمل. أحصل على نفس الرسالة.
لدي "type": "module" في package.json وباقي مشروعي يستخدم وحدات ، لذا فإن التغيير ليس خيارًا.

إذا استخدمت أيًا مما يلي:

import cucumber from '@cucumber/cucumber';
// OR
import { When } from '@cucumber/cucumber';

أحصل على نفس رسالة الخطأ مثل المنشور الأصلي.
إذا غيرت إلى

const cucumber = require('@cucumber/cucumber');

ثم أتلقى هذه الرسالة:

بدلاً من ذلك ، أعد تسمية /some/path/cucumbertest/features/step_definitions.js لتنتهي بـ .cjs أو غيّر الكود المطلوب لاستخدام import () أو أزل "type": "module" من project / package.json.

إذا قمت بإعادة تسمية step_definitions.js إلى _either_ step_definitions.cjs أو step_definitions.mjs ، فسيتم تجاهلها فقط وأحصل على رسائل عامة من الخيار تقول لإنشاء بذرة لخطواتي:

UUUUUU

Failures:

1) Scenario: Empty Payload # spec\cucumber\features\users\create\main.feature:4
   ? When the client creates a POST request to /users
       Undefined. Implement with the following snippet:

         When('the client creates a POST request to \/users', function () {
           // Write code here that turns the phrase above into concrete actions
           return 'pending';
         });

... etc

أحصل على نفس الرسائل بالضبط إذا قمت بحذف الملف تمامًا ، لذلك من الواضح أنه يتم تجاهله.

الحل (نوع من)

في النهاية ، الطريقة الوحيدة التي تمكنت من خلالها من تشغيل الخيار في مشروع باستخدام "type": "module" هي إنشاء specpackage.json منفصلة بدون "type": "module" وتثبيت cucumber في spec/node_modules .

حتى الآن يبدو أنها تعمل. دعنا نرى ما إذا كان لا يزال يعمل بمجرد إنشاء بعض الاختبارات الفعلية.

إلى القائمين على الصيانة: هذه هي المرة الأولى التي أستخدم فيها الخيار ، وأنا أحبه حتى الآن. ومع ذلك ، فقد أثر هذا حقًا في تقدمي. بدلاً من قضاء فترة الظهيرة في تعلم الخيار ، أمضيته في تصحيح أخطاء الواردات. أعلم كم هو مزعج محاولة إنشاء مكتبة تدعم كلاً من واردات ES6 و CJS حتى يكون لديك تعاطفي.

حل ممكن:
من الممكن أن تنشر كلاً من CJS و ES6 في وقت واحد إذا أنشأت ملف توزيع ثانٍ:

Dist / cucumber.js
dist / cucumber.module.js

ثم أضف هذا السطر إلى package.json:

"الوحدة": "dist / cucumber.module.js"

يجب أن يؤدي هذا إلى تمكين العقدة لحل واردات ES6.

يجب أن يكون هناك طريقة أفضل للقيام بذلك. أنا أيضًا أواجه هذه المشكلة ، ولأن الواردات لا يمكن استخدامها مع CommonJS سأضطر إلى استخدامها
https://nodejs.org/api/esm.html#esm_import_expressions

في كل مكان...

هل هناك طريقة يمكن بها أن يكون للخيار علامة تساعدنا على استخدام ES؟

سريع واحد للأشخاص المهتمين بهذا: عند استخدام ESM في مشروع مع الخيار ، هل يمكنك:

  • استخدم الامتداد .js و "type":"module"
  • استخدم الامتداد .mjs
  • شيء آخر؟

شكرا!

أميل إلى اتباع اصطلاح استخدام امتدادات .mjs و .cjs لجميع ملفات JavaScript الخاصة بي لتجنب الغموض ، وسيكون من المفضل القيام بذلك في هذه الحالة أيضًا. ومع ذلك ، لن يكون من الإزعاج الكبير تعيين "type": "module" على مستوى المشروع وترك هذه الملفات كملفات js. إذا كان هذا هو الحل الأفضل لمزيد من المستخدمين.

أستخدم الامتداد .js و "type":"module" لأنني أتوقع أن يصبح هذا هو القاعدة على المدى الطويل.

شكرا لردود الفعلlooeeeadamlacoste

davidjgoss هل يكفي دمج # 1589

شكرًا على التنبيه @ aurelien-reeves!

الإصدار 7.2.0 من @cucumber/cucumber متوفر الآن على npm ، بما في ذلك الدعم التجريبي لـ ESM كما هو موضح في المستندات هنا:
https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#es-modules-experimental-nodejs-12

إذا كان بإمكان المهتمين تجربة ذلك والإبلاغ عما تجده سيكون رائعًا. أضع هنا نموذجًا مبسطًا للغاية لمشروع:
https://github.com/davidjgoss/cucumber-esm-example

يسعدنا إبقاء هذه المشكلة مفتوحة لبعض الوقت فقط للقبض على التعليقات المبكرة في مكان واحد.

لذلك تسبب هذا التغيير في مشكلة مع أطر عمل الجهات الخارجية والمنسقات التي تحتوي على require أشياء معينة بشكل مباشر (على عكس من نقطة الدخول). لقد أصدرت 7.2.1 وهو في الأساس عودة إلى 7.1.0 لإلغاء حظر هؤلاء الأشخاص ، وسأبحث في السبب وأرى ما إذا كان بإمكاننا تجنبه مع الاستمرار في دعم ESM. في غضون ذلك ، إذا لم يؤثر ذلك عليك ، فلا يزال هناك 7.2.0 لتجربته.

لقد كنت أحاول إعطاء v7.2.0 فرصة مع العلم --esm . أستخدم testdouble في اختبارات الحزمة التي أحاول تحويلها إلى ESM.

من أجل استخدام td.replaceEsm ، يجب استخدام محمل مثل --loader=testdouble . عندما أحاول توفير اللودر مباشرة إلى cucumber cli ، أحصل على الخطأ التالي:

> cucumber-js test/integration --esm --loader=testdouble

error: unknown option '--loader=testdouble'

نظرًا لأن هذا الخيار لم يكن متاحًا ، فقد حاولت بعد ذلك باستخدام NODE_OPTIONS :

> NODE_OPTIONS='--loader=testdouble' cucumber-js test/integration --esm

(node:62231) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
internal/process/esm_loader.js:74
    internalBinding('errors').triggerUncaughtException(
                              ^

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for /path/to/project/node_modules/@cucumber/cucumber/bin/cucumber-js
    at defaultGetFormat (internal/modules/esm/get_format.js:71:15)
    at getFormat (file:///path/to/project/node_modules/quibble/lib/quibble.mjs:65:12)
    at Loader.getFormat (internal/modules/esm/loader.js:104:42)
    at Loader.getModuleJob (internal/modules/esm/loader.js:242:31)
    at async Loader.import (internal/modules/esm/loader.js:176:17)
    at async Object.loadESM (internal/process/esm_loader.js:68:5) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}

كمرجع ، كان هذا ضد العقدة v14.17.0

سيكون من الجيد أن يعمل هذا مع نهج NODE_OPTIONS ، ولكن من الناحية المثالية ، سيتم دعم خيار --loader مباشرة من قبل cli مثل بعض أطر الاختبار التي بدأت في الدعم. يرجى مراعاة هذه الأساليب مع إصدارك التالي الذي يقترب من دعم ESM :)

هل هناك طريقة لتعريف محمل تتوقع أن تعمل به بـ v7.2.0 ؟

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات