ليس لدينا حاليًا طريقة جيدة لتشغيل cucumber-js برمجيًا. الحاجة من زاويتين:
ما يحدث في الوقت الحالي هو إنشاء مثيل جديد من Cli
باستخدام مدخلات argv مجمعة معًا . من الواضح أنه غير مرهون للغاية ولا يوجد أيضًا على واجهة برمجة التطبيقات العامة .
في بعض الأحيان (ربما بسبب الهشاشة المتصورة لما ورد أعلاه) ستعتمد الأطر فقط على Cucumber-js CLI ولكنها تكافح لإيجاد طرق للتكامل ولديها خياراتها الخاصة.
تعتبر فئة Runtime
حاليًا جزءًا من واجهة برمجة التطبيقات العامة ولكنها ليست مفيدة في هذه السياقات ، اعتمادًا على المخللات ورمز الدعم الذي سيوفره المتصل.
مكونان في المشروع:
runCucumber
وظيفة غير متزامنة جديدة تنفذ اختبار تشغيل قيد التشغيل. المسؤوليات:
سيكون هذا جزءًا من واجهة برمجة التطبيقات العامة ونحن نشجع المشرفين على إطار العمل لاستخدامه عند "تغليف" cucumber-js. سنستخدمه أيضًا للاختبار الخاص بنا.
قدر الإمكان ، سيتجنب التفاعل المباشر مع process
، بدلاً من قبول الخيارات العادية وواجهات التدفق للإخراج ، وترك الأمر للمتصل ليقرر كيفية الخروج بناءً على النتيجة أو خطأ غير معالج.
يجب أيضًا إزالة Runtime
من واجهة برمجة التطبيقات العامة لأنها في الحقيقة شيء داخلي.
بشكل فعال "عميل" runCucumber
. المسؤوليات:
runCucumber
مع الخيارات التي تم حلهاسيستمر هذا في عدم وجوده على واجهة برمجة التطبيقات العامة. كما أنها ستستخدم فقط الوظائف / الواجهات الموجودة على واجهة برمجة التطبيقات العامة ، بحيث يمكننا بسهولة تقسيمها إلى حزمة خاصة بها في مرحلة ما ، كما هو الحال الآن مع مشاريع مثل Jest .
يمهد هذا الفصل أيضًا الطريق لبعض ميزات CLI الجديدة المثيرة للاهتمام دون جعلها تتسرب إلى الأجزاء الداخلية ، على سبيل المثال:
--gui
لأشياء الخيار والإلكترون--interactive
لعمليات إعادة التشغيل السريعة المستهدفة عند TDD'ingنكشف أيضًا عن الوظائف (التي يستهلكها CLI والآخرون) من أجل:
i18nKeywords
و i18nLanguages
سنستهدف هذا في إصدار 8.0.0 القادم. أنا مستعد للبدء في هذا اليوم.
ترحيل لردود الفعل الأولية:aslakhellesoycharlierudolph @ أوريليان-ريفزmattwynnenicojs @ يناير-molak
أنا أحب هذا الاقتراح! نحن بالفعل واجهة برمجة تطبيقات مثل هذا في RunCucumber الخاص بـ fake-cucumber
davidjgoss - يبدو رائعا!
كمرجع لك ، إليك كيفية استدعاء Serenity / JS للخيار في الوقت الحالي - CucumberCLIAdapter
وإليك المنطق حول تحويل معلمات التكوين إلى argv - CucumberOptions
.
أن تكون قادرًا على توفير كائن خيارات بدلاً من argv سيكون أجمل بكثير
أحبها!
أثناء تحديد واجهة برمجة التطبيقات العامة الجديدة ، قد نفكر أيضًا في ما حدث مؤخرًا مع المشكلة رقم 1489 ونفكر في توفير واجهات برمجة تطبيقات عامة للحصول على تفاعل أكثر وأفضل مع المرشحات والميزات الناتجة قيد الاختبار
ولإضافة إلى ذلك - دعونا نصنع شيئًا يسهل على Cucumber-Electron أيضًا:
إن امتلاك واجهة برمجة تطبيقات عامة أفضل من لا شيء ، لذا انطلق 👍!
ويفضل أن يكون لدي أيضًا واجهة برمجة تطبيقات لتحميل ملفات التعريف باستخدام نفس القواعد التي يستخدمها cucumber-js
، لذا يمكنني تقليد السلوك الدقيق لاستدعاء cucumber-js العادي.
loadProfiles(directory = process.cwd()): Record<string, Profile>
ستعتمد StrykerJS أيضًا بشكل كبير على واجهة برمجة تطبيقات custom_formatters والأحداث المنشورة بواسطة eventBroadcaster
. هل يمكننا إضافة هذه إلى واجهة برمجة التطبيقات العامة أيضًا؟ انظر: https://github.com/stryker-mutator/stryker-js/blob/03b1f20ed933d3a50b52022cfe363c606c2b16c5/packages/cucumber-runner/src/stryker-formatter.ts#L45 -L69
ويفضل أن يكون لدي أيضًا واجهة برمجة تطبيقات لتحميل ملفات التعريف باستخدام نفس القواعد التي يستخدمها cucumber-js ، حتى أتمكن من محاكاة السلوك الدقيق لاستدعاء cucumber-js العادي.
هذه نقطة جيدة. تقترن الملفات الشخصية (في شكلها الحالي على الأقل) بشكل أساسي بـ CLI لذا من الصواب الاحتفاظ بها على هذا الجانب من الحدود ، ولكن لا يزال بإمكاننا كشف وظيفة لتحميلها وإنشاء كائن خيارات جزئي.
أثناء تحديد واجهة برمجة التطبيقات العامة الجديدة ، قد نفكر أيضًا في ما حدث مؤخرًا مع المشكلة رقم 1489 ونفكر في توفير واجهات برمجة تطبيقات عامة للحصول على تفاعل أكثر وأفضل مع المرشحات والميزات الناتجة قيد الاختبار.
أعتقد أنه يمكننا تضمين خيار لتوفير مرشح مخلل مخصص عند استدعاء واجهة برمجة التطبيقات (بالإضافة إلى الأسماء والعلامات وما إلى ذلك التي تدفع التصفية المدمجة).
الصيغة الحالية لملفات التعريف إذا كانت سطر الأوامر y.
سأكون قادرًا على تحديد ملفات التعريف بتنسيق أكثر عمومية مثل JSON أو JavaScript أو YAML أو متغيرات البيئة. في JSON يمكن أن يبدو كالتالي:
.cucumber.json
{
"default": {
"requireModule": ["ts-node/register"],
"require": ["support/**/*./ts"],
"worldParameters": {
"appUrl": "http://localhost:3000/",
},
"format": ["progress-bar", "html:./cucumber-report.html"]
},
"ci": {
"requireModule": ["ts-node/register"],
"require": ["support/**/*./ts"],
"worldParameters": {
"appUrl": "http://localhost:3000/",
},
"format": ["html:./cucumber-report.html"],
"publish": true
}
}
أو باستخدام JavaScript
.cucumber.js
const common = {
"requireModule": ["ts-node/register"],
"require": ["support/**/*./ts"],
"worldParameters": {
"appUrl": "http://localhost:3000/",
}
}
module.exports = {
default: {
...common,
"format": ["progress-bar", "html:./cucumber-report.html"]
},
ci: {
...common,
"format": ["html:./cucumber-report.html"],
"publish": true
}
}
أو حتى مع متغيرات البيئة (على سبيل المثال محملة بأداة مثل dotenv):
.cucumber.env
CUCUMBER_PROFILE_DEFAULT_REQUIREMODULE=ts-node/register
CUCUMBER_PROFILE_DEFAULT_REQUIRE=ts-node/register
CUCUMBER_PROFILE_DEFAULT_WORLDPARAMETERS_APPURL=http://localhost:3000/
CUCUMBER_PROFILE_DEFAULT_FORMAT=progress-bar,html:./cucumber-report.html
CUCUMBER_PROFILE_CI_REQUIREMODULE=ts-node/register
CUCUMBER_PROFILE_CI_REQUIRE=ts-node/register
CUCUMBER_PROFILE_CI_WORLDPARAMETERS_APPURL=http://localhost:3000/
CUCUMBER_PROFILE_CI_FORMAT=progress-bar,html:./cucumber-report.html
CUCUMBER_PROFILE_CI_PUBLISH=true
في الواقع ، تقوم مكتبة التكوين بهذا بالضبط. لم ننتهي أبدًا بدمجه في Cucumber-JVM لأن أشياء أخرى تعترض طريقنا ، لكن ربما يمكننا أن نجربها باستخدام تطبيق JavaScript؟
aslakhellesoy توافق على أنه سيكون رائع! سأحاول الحصول على POC لهذا الاقتراح لذلك لدينا شيء أكثر واقعية للتحدث حوله ، ونود أن نجعل الملفات الشخصية جزءًا منه (4.5 سنوات والاعتماد على # 751 😄)
الحكام. # 1004
هذه نقطة جيدة. تقترن الملفات الشخصية (في شكلها الحالي على الأقل) بشكل أساسي بـ CLI لذا من الصواب الاحتفاظ بها على هذا الجانب من الحدود ، ولكن لا يزال بإمكاننا كشف وظيفة لتحميلها وإنشاء كائن خيارات جزئي.
نعم ، سيكون ذلك رائعًا ويحظى بتقدير كبير من وجهة نظر منشئي المكونات الإضافية.
سأكون قادرًا على تحديد ملفات التعريف بتنسيق أكثر عمومية مثل JSON أو JavaScript أو YAML أو متغيرات البيئة. في JSON يمكن أن يبدو كالتالي:
هذا يبدو رائعًا! وهو أيضًا سبب تقديري لواجهة برمجة التطبيقات لتحميلها بنفس الطريقة التي يقوم بها cucumberJS. تحميل ملف واحد cucumber.js
تافه. يعد تكرار خوارزمية تحميل ملف التكوين ، بما في ذلك الأسبقية وتنسيق الملف وما إلى ذلك والحفاظ عليه شيئًا آخر تمامًا 😅.
س: هل سأتمكن من تشغيل runCucumber
مرتين متتاليتين _ دون مسح ذاكرة التخزين المؤقت المطلوبة _؟ هذا مهم لحالة استخدام اختبار الطفرات.
نريد تحميل البيئة وتشغيل الاختبارات عدة مرات في تتابع سريع أثناء تغيير متغير عالمي من أجل تبديل المسوخ النشط.
في الوقت الحالي ، نستخدم واجهة برمجة التطبيقات الخاصة cli
ونحتاج إلى مسح ملفات تعريف الخطوة من require.cache
بين كل تشغيل اختباري. هذا ليس مثاليًا لـ CommonJS ولن يعمل على الإطلاق لـ esm.
الكود الزائف لحالة الاستخدام الخاصة بنا:
const profiles = await loadProfiles();
const options = {
...profiles.default,
formatter: require.resolve('./our-awesomely-crafted-formatter'),
some: 'other options we want to override',
}
const cucumber = new Cucumber(options);
// Allow cucumber to load the step definitions once.
// This is `async`, so support for esm can be added without a breaking change
await cucumber.initialize();
// Initial test run ("dry run"), without mutants active
await cucumber.run();
collectMutantCoveragePerTestFromFormatter();
// Start mutation testing:
global.activeMutant = 1;
await cucumber.run({ features: ['features/a.feature:24']);
collectResultsFromFormatterToDetermineKilledOrSurvivedMutant()
global.activeMutant = 2;
await cucumber.run({ features: ['features/b.feature:24:25:26', 'features/c.feature:12']);
collectResultsFromFormatterToDetermineKilledOrSurvivedMutant()
// etc
نوافق علىnicojs بالتأكيد أننا بحاجة إلى هذا ، لقد تم
ما كنت قد رسمته حتى الآن كان أكثر من أسلوب وظيفي ولكن نفس المفهوم الأساسي على ما أعتقد:
const runnerOptions = {
support: {
require: ['features/support/**/*.js']
}
}
// initial run returns support code library
const { support } = await runCucumber(runnerOptions)
// subsequent run reuses support code library
await runCucumber({
...runnerOptions,
support
})
هذا يعمل معنا 👍
يعمل معنا أيضًا 👍🏻
أعتقد حقًا أن شيئًا كهذا سيكون مفيدًا للغاية كبديل للفوضى العظيمة التي نعيشها اليوم من خلال اختبار تكامل الأدوات (Jest ، Cypress) ، على سبيل المثال ، وجدت هذه المشكلات (بترتيب الأهمية):
cypress-cucumber-preprocessor
العلامات الموجودة في الأمثلة (https://github.com/TheBrainFamily/cypress-cucumber-preprocessor/issues/196)jest-cucumber
الإبلاغ عن خيار JSON (https://github.com/bencompton/jest-cucumber/issues/27)cypress-cucumber-preprocessor
عدة تقارير JSON للخيار بدون دعم رسمي للتجميع (https://github.com/TheBrainFamily/cypress-cucumber-preprocessor/issues/423)jest-cucumber
ليس ملائمًا مثل jest-cucumber-fusion
cucumber-jest
...أفضل أن أرى بعض كود الغراء الأدنى بين Jest / Karma / Cypress / إلخ. و cucumber-js حتى لا أعاني من كل تلك الميزات المفقودة التي أحتاج إلى استخدامها.
اقتراح عظيم @ davidjgoss 👍
هذا الفصل في الاهتمامات بين واجهة مستخدم سطر الأوامر و "منطق العمل" لتحليل السيناريوهات وتنفيذها كاختبارات تذكرني بنمط البنية السداسية .
في cucumber-ruby ، قمنا بتقسيم منطق المجال الأساسي (أو "السداسي الداخلي") إلى حزمة جوهرة منفصلة ، حيث كنا نعيد بنائها من نقطة الصفر في "غرفة نظيفة". أدرك أن هذا ليس هو السياق هنا ، ولكن قد يكون من المفيد استلهام بعض الإلهام أو إعادة تغذية الابتكارات من هذا التصميم إلى واجهة برمجة تطبيقات Ruby. يوجد مثال في README الخاص بجوهرة الخيار والياقوت الأساسية حول كيفية استخدام واجهة برمجة التطبيقات هذه.
حسنًا ، هذا هو المرور الأول في توقيع API لجزء "التشغيل". يعتمد بشكل كبير على الكائن IConfiguration
الذي لدينا داخليًا (لذلك لا ينبغي أن يتسبب في إعادة بناء حجم كبير جدًا لأسفل) ولكن أقل "ثابت" قليلاً:
export interface IRunCucumberOptions {
cwd: string
features: {
defaultDialect?: string
paths: string[]
}
filters: {
name?: string[]
tagExpression?: string
}
support:
| {
transpileWith?: string[]
paths: string[]
}
| ISupportCodeLibrary
runtime: {
dryRun?: boolean
failFast?: boolean
filterStacktraces?: boolean
parallel?: {
count: number
}
retry?: {
count: number
tagExpression?: string
}
strict: boolean
worldParameters?: any
}
formats: {
stdout: string
files: Record<string, string>
options: IParsedArgvFormatOptions
}
}
export interface IRunResult {
success: boolean
support: ISupportCodeLibrary
}
export async function runCucumber(
options: IRunCucumberOptions
): Promise<IRunResult> {
// do stuff
}
واستخدام مثال مفتعل للغاية:
const result = await runCucumber({
cwd: process.cwd(),
features: {
paths: ['features/**/*.feature'],
},
filters: {
name: ['Acme'],
tagExpression: '<strong i="10">@interesting</strong>',
},
support: {
transpileWith: ['ts-node'],
paths: ['features/support/**/*.ts'],
},
runtime: {
failFast: true,
retry: {
count: 1,
tagExpression: '<strong i="11">@flaky</strong>',
},
strict: true,
worldParameters: {
foo: 'bar',
},
},
formats: {
stdout: '@cucumber/pretty-formatter',
files: {
'report.html': 'html',
'TEST-cucumber.xml': 'junit',
},
options: {
printAttachments: false,
},
},
})
نرحب بالتعليقات! لاحظ أن هذا لا يغطي عناصر تحميل الملف الشخصي / التكوين والتي ستكون وظيفة أخرى.
أعتقد أن هذا يبدو جيدًا. سؤال: كيف يمكنني تكوين منسق مخصص؟
nicojs شيء مثل على CLI
formats: {
files: {
'report.html': './my/fancy-reporter.js',
'other-report.html': '@me/reporter-package',
}
},
من الرائع رؤية التقدم في هذاdavidjgoss!
لا أريد إبطاء التقدم في هذا الأمر ، ولكن في نفس الوقت أريد التأكد من اعتمادنا تنسيقًا يمكن أن يعمل مع تطبيقات Cucumber الأخرى أيضًا.
في النهاية مخطط JSON ، ولكن أثناء مناقشته ، أعتقد أن أنواع TypeScript أسهل بالنسبة لنا كبشر في التحليل.
أقترح إنشاء مشكلة جديدة حول التنسيق المقترح في cucumber/common
monorepo وندعو الفريق الأساسي للمناقشة هناك.
aslakhellesoy سوف تفعل.
ما رأيك في عدم ربط واجهة برمجة التطبيقات البرمجية بهيكل الخيارات الشائعة؟ مثلما كنا نرسم من ذلك إلى خيارات runCucumber
. ربما تضيف بعض التعقيد ولكنها جذابة بسبب أشياء مثل وجود كتلة support
تكون إما معلمات يتم تحميلها أو مكتبة رموز دعم تم تحميلها مسبقًا. يمكن أن تفعل الشيء نفسه بالنسبة للميزات + المخللات أيضًا. وهناك العديد من الخيارات التي ندعمها على CLI (على سبيل المثال --exit
) والتي لا تناسب واجهة برمجة التطبيقات البرمجية.
ما رأيك في عدم ربط واجهة برمجة التطبيقات البرمجية بهيكل الخيارات الشائعة؟
أعتقد أن هذا جيد ، طالما أننا نقدم وظيفة تتحول من محتويات ملف الخيارات إلى بنية البيانات runCucumber
يريدها.
في النهاية مخطط JSON ، ولكن أثناء مناقشته ، أعتقد أن أنواع TypeScript أسهل بالنسبة لنا كبشر في التحليل.
لماذا نحتاج للاختيار؟ نحن نستخدم مخطط JSON في StrykerJS لإنشاء نص مكتوب باستخدام prebuild
الخطوة.
لا تزال مخططات JSON قابلة للقراءة إلى حد ما بالنسبة للبشر IMO. لدينا بالفعل علاقات عامة على Stryker repo ويبدو أن الناس يعرفون ما يجب عليهم فعله 🤷♀️
شيء مثل على CLI
formats: { files: { 'report.html': './my/fancy-reporter.js', 'other-report.html': '@me/reporter-package', } },
كيف يعمل هذا لمراسل لا يحتاج إلى اسم ملف الإخراج؟ مثل ذلك:
formats: {
files: {
'': require.resolve('./my-awesome-stryker-formatter')
}
}
لماذا نحتاج للاختيار؟
أعتقد أننا يجب أن نستخدم مخطط JSON كمصدر وحيد للحقيقة لهيكل التكوين. -ثم قم بإنشاء TypeScript / Java / Whatever code من هذا المخطط.
لكن مخطط JSON يصعب قراءته قليلاً بالنسبة للبشر ، لذلك بينما نناقش المخطط في مشكلة GitHub في cucumber/common
كنت أقترح TypeScript لتسهيل المناقشة.
هل تفهم ما اعني؟
لا تزال مخططات JSON قابلة للقراءة إلى حد ما بالنسبة للبشر IMO
ليس لي :-) مطول جدا.
كيف يعمل هذا لمراسل لا يحتاج إلى اسم ملف الإخراج؟
formats: {
stdout: './my-awesome-stryker-formatter',
files: {
'report.html': './my/fancy-reporter.js',
'other-report.html': '@me/reporter-package',
}
},
(يمكن لمنسق واحد فقط استخدام تيار stdout)
التعليق الأكثر فائدة
(يمكن لمنسق واحد فقط استخدام تيار stdout)