@vardot/varbase-e2e 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +148 -0
- package/CLAUDE.md +544 -0
- package/README.md +22 -0
- package/bin/generate-reports.js +363 -0
- package/bin/init-varbase-e2e.js +330 -0
- package/bin/postinstall.js +26 -0
- package/cucumber.js +125 -0
- package/docs/00-quick-start.md +102 -0
- package/docs/01-getting-started.md +102 -0
- package/docs/02-bbr-smart-waits.md +77 -0
- package/docs/03-selector-registry.md +95 -0
- package/docs/04-step-reference.md +801 -0
- package/docs/05-web-first-assertions.md +91 -0
- package/docs/06-network-and-dialogs.md +83 -0
- package/docs/07-auth-state.md +75 -0
- package/docs/08-clock-mocking.md +59 -0
- package/docs/09-api-testing.md +114 -0
- package/docs/10-accessibility.md +167 -0
- package/docs/11-debugging.md +129 -0
- package/docs/12-ai-agent-guide.md +338 -0
- package/docs/13-faq.md +236 -0
- package/docs/14-recipes-cookbook.md +255 -0
- package/docs/15-tag-conventions.md +76 -0
- package/docs/16-ci-cd.md +90 -0
- package/docs/README.md +125 -0
- package/docs/advanced-screenshots/then-i-save-fullscreen-screenshot.md +36 -0
- package/docs/advanced-screenshots/then-i-save-width-x-height-screenshot.md +34 -0
- package/docs/advanced-screenshots/when-i-save-screenshot-name-filename.md +45 -0
- package/docs/advanced-screenshots.md +52 -0
- package/docs/advanced-selectors.md +18 -0
- package/docs/api-step-definitions.md +45 -0
- package/docs/global-settings.md +267 -0
- package/docs/install-varbase-e2e/ddev-varbase-e2e.md +52 -0
- package/docs/install-varbase-e2e.md +34 -0
- package/docs/overview.md +40 -0
- package/docs/step-definitions/given-i-am-anonymous-user.md +67 -0
- package/docs/step-definitions/given-i-am-homepage.md +68 -0
- package/docs/step-definitions/given-i-am-specific-page.md +72 -0
- package/docs/step-definitions/then-i-should-be.md +56 -0
- package/docs/step-definitions/then-i-should-see.md +62 -0
- package/docs/step-definitions/then-item-checkbox.md +54 -0
- package/docs/step-definitions/then-item-link-should-contain-url.md +84 -0
- package/docs/step-definitions/then-item-should-contain.md +67 -0
- package/docs/step-definitions/then-radio-button-value-should-be-selected.md +68 -0
- package/docs/step-definitions/then-response-status-code-should-be-number.md +69 -0
- package/docs/step-definitions/then-value-radio-button-selected.md +62 -0
- package/docs/step-definitions/when-i-attach-file-file-name-element.md +71 -0
- package/docs/step-definitions/when-i-check-checkbox.md +60 -0
- package/docs/step-definitions/when-i-click-link.md +51 -0
- package/docs/step-definitions/when-i-click-operation-text-row.md +71 -0
- package/docs/step-definitions/when-i-fill.md +59 -0
- package/docs/step-definitions/when-i-go-homepage.md +60 -0
- package/docs/step-definitions/when-i-go-specific-page.md +67 -0
- package/docs/step-definitions/when-i-move-backward-one-page.md +49 -0
- package/docs/step-definitions/when-i-move-forward-one-page.md +58 -0
- package/docs/step-definitions/when-i-press-button.md +58 -0
- package/docs/step-definitions/when-i-reload-page.md +51 -0
- package/docs/step-definitions/when-i-scroll.md +73 -0
- package/docs/step-definitions/when-i-select-option-select-list.md +53 -0
- package/docs/step-definitions/when-i-select-radio-button-value.md +62 -0
- package/docs/step-definitions/when-i-uncheck-checkbox.md +57 -0
- package/docs/step-definitions/when-i-wait-ajax-finish.md +64 -0
- package/docs/step-definitions/when-i-wait.md +62 -0
- package/docs/step-definitions.md +82 -0
- package/docs/steps/README.md +137 -0
- package/docs/steps/a11y.md +731 -0
- package/docs/steps/action.md +181 -0
- package/docs/steps/api.md +679 -0
- package/docs/steps/assertion.md +328 -0
- package/docs/steps/auth.md +108 -0
- package/docs/steps/clock.md +171 -0
- package/docs/steps/cookie.md +293 -0
- package/docs/steps/debug.md +52 -0
- package/docs/steps/dialog.md +226 -0
- package/docs/steps/drupal-canvas.md +414 -0
- package/docs/steps/drupal-ckeditor.md +118 -0
- package/docs/steps/drupal-core.md +688 -0
- package/docs/steps/drupal-layout-builder.md +437 -0
- package/docs/steps/drupal-media.md +107 -0
- package/docs/steps/drupal-moderation.md +81 -0
- package/docs/steps/drupal-paragraphs.md +36 -0
- package/docs/steps/element.md +455 -0
- package/docs/steps/field.md +648 -0
- package/docs/steps/file-download.md +235 -0
- package/docs/steps/form.md +344 -0
- package/docs/steps/iframe.md +248 -0
- package/docs/steps/input.md +220 -0
- package/docs/steps/javascript.md +111 -0
- package/docs/steps/keyboard.md +107 -0
- package/docs/steps/link.md +217 -0
- package/docs/steps/metatag.md +99 -0
- package/docs/steps/modal.md +232 -0
- package/docs/steps/navigation.md +275 -0
- package/docs/steps/network.md +284 -0
- package/docs/steps/path.md +203 -0
- package/docs/steps/response.md +102 -0
- package/docs/steps/responsive.md +139 -0
- package/docs/steps/rest.md +140 -0
- package/docs/steps/screenshot.md +211 -0
- package/docs/steps/scroll.md +266 -0
- package/docs/steps/selectors.md +848 -0
- package/docs/steps/storage.md +254 -0
- package/docs/steps/table.md +216 -0
- package/docs/steps/varbase.md +391 -0
- package/docs/steps/video.md +111 -0
- package/docs/steps/wait.md +553 -0
- package/docs/steps/web-first.md +289 -0
- package/docs/steps/xml.md +505 -0
- package/docs/steps/yaml.md +951 -0
- package/examples/a11y.html +31 -0
- package/examples/about-us.html +11 -0
- package/examples/account.html +32 -0
- package/examples/ajax-wait-examples.html +147 -0
- package/examples/contact-us.html +10 -0
- package/examples/cookies.html +10 -0
- package/examples/date.html +12 -0
- package/examples/dynamic-examples.html +498 -0
- package/examples/element.html +11 -0
- package/examples/example-api.json +28 -0
- package/examples/existing-page.html +11 -0
- package/examples/field.html +24 -0
- package/examples/index.html +37 -0
- package/examples/keyboard.html +11 -0
- package/examples/links.html +9 -0
- package/examples/modal.html +14 -0
- package/examples/path.html +3 -0
- package/examples/table.html +13 -0
- package/examples/test--modal-dialogs.html +252 -0
- package/examples/test--then--i-should--not--see-text-in-element.html +48 -0
- package/examples/test--then--i-should--see-text-in-element.html +48 -0
- package/examples/test--then--i-should--see-text-matching-pattern.html +31 -0
- package/examples/test--then--i-should-see-text-in-table-row.html +232 -0
- package/examples/test--then--the-checkbox-checked.html +68 -0
- package/examples/test--then--the-element-should_not-contain-cssproperty.html +47 -0
- package/examples/test--then--the-link-should-contain.html +21 -0
- package/examples/test--when--i-additionally-select.html +27 -0
- package/examples/test--when--i-attach-file-to-feild.html +50 -0
- package/examples/test--when--i-check-uncheck-checkbox.html +47 -0
- package/examples/test--when--i-click-link.html +18 -0
- package/examples/test--when--i-click-text-in-table-row.html +178 -0
- package/examples/test--when--i-fill-in.html +48 -0
- package/examples/test--when--i-press-button.html +31 -0
- package/examples/test--when--i-reload-page.html +27 -0
- package/examples/test--when--i-scroll-functionality.html +177 -0
- package/examples/test--when--i-scroll-left-right-functionality.html +149 -0
- package/examples/test--when--i-select-option-from-dropdownlist.html +35 -0
- package/examples/test--when--i-select-radio-button.html +95 -0
- package/examples/test--when-i-wait-max-of-time.html +39 -0
- package/examples/test--when-i-wait-page-until-is-loaded.html +47 -0
- package/examples/test--when-i-wait-specific-time.html +39 -0
- package/examples/test-acceptable-url-path.html +11 -0
- package/examples/video-demo.html +43 -0
- package/package.json +56 -0
- package/playwright.config.ts +39 -0
- package/tests/assets/test-document.pdf +14 -0
- package/tests/assets/vardot.png +0 -0
- package/tests/features/a11y.feature +39 -0
- package/tests/features/ajax-wait-examples.feature +24 -0
- package/tests/features/api-step-definitions-individual.feature +185 -0
- package/tests/features/complete-api-testing-examples.feature +205 -0
- package/tests/features/cookie.feature +13 -0
- package/tests/features/date.feature +11 -0
- package/tests/features/dynamic-content-examples.feature +156 -0
- package/tests/features/element.feature +16 -0
- package/tests/features/field.feature +25 -0
- package/tests/features/keyboard.feature +10 -0
- package/tests/features/link.feature +11 -0
- package/tests/features/modal-advanced-example.feature +54 -0
- package/tests/features/modal-basic-example.feature +42 -0
- package/tests/features/modal-error-handling-example.feature +37 -0
- package/tests/features/modal.feature +11 -0
- package/tests/features/path.feature +16 -0
- package/tests/features/response.feature +8 -0
- package/tests/features/responsive.feature +10 -0
- package/tests/features/table.feature +18 -0
- package/tests/features/test--given--i-am-an-anonymous-user.feature +10 -0
- package/tests/features/test--given--i-am-on-homepage.feature +16 -0
- package/tests/features/test--then--i-save-screenshot.feature +33 -0
- package/tests/features/test--then--i-see-locator.feature +320 -0
- package/tests/features/test--then--i-should--not--see-text-in-element.feature +13 -0
- package/tests/features/test--then--i-should--see-text-in-element-by-attr.feature +10 -0
- package/tests/features/test--then--i-should--see-text-in-element.feature +10 -0
- package/tests/features/test--then--i-should--see-text-matching-pattern-in-element.feature +13 -0
- package/tests/features/test--then--i-should--see-text-matching-pattern.feature +18 -0
- package/tests/features/test--then--i-should-be-on-page.feature +10 -0
- package/tests/features/test--then--i-should-be-on-the-homepage.feature +10 -0
- package/tests/features/test--then--i-should-not-see-an-element-by-attr.feature +8 -0
- package/tests/features/test--then--i-should-not-see-an-element.feature +8 -0
- package/tests/features/test--then--i-should-see-an-element-by-attr.feature +8 -0
- package/tests/features/test--then--i-should-see-an-element.feature +8 -0
- package/tests/features/test--then--i-should-see-n-elements.feature +24 -0
- package/tests/features/test--then--i-should-see-text-in-table-row.feature +96 -0
- package/tests/features/test--then--print-current-url-and-response.feature +21 -0
- package/tests/features/test--then--the-checkbox-is-checked.feature +18 -0
- package/tests/features/test--then--the-checkbox-should-be-checked.feature +16 -0
- package/tests/features/test--then--the-element-should-contain-cssproperty.feature +9 -0
- package/tests/features/test--then--the-element-should-not-contain-cssproperty.feature +10 -0
- package/tests/features/test--then--the-field-should-contain-value.feature +11 -0
- package/tests/features/test--then--the-response-should-contain.feature +11 -0
- package/tests/features/test--then--the-response-status-code-should-be.feature +31 -0
- package/tests/features/test--then--the-url-should-match.feature +7 -0
- package/tests/features/test--then-the-link-should-contain-by-attribute.feature +18 -0
- package/tests/features/test--then-the-link-should-contain.feature +7 -0
- package/tests/features/test--when--i-additionally-select.feature +30 -0
- package/tests/features/test--when--i-attach-file-to-feild.feature +9 -0
- package/tests/features/test--when--i-check-checkbox.feature +21 -0
- package/tests/features/test--when--i-click-link-by-attribute.feature +21 -0
- package/tests/features/test--when--i-click-link.feature +9 -0
- package/tests/features/test--when--i-click-text-in-table-row.feature +37 -0
- package/tests/features/test--when--i-fill-in-by-attr.feature +31 -0
- package/tests/features/test--when--i-fill-in-table-by-attr.feature +19 -0
- package/tests/features/test--when--i-fill-in.feature +32 -0
- package/tests/features/test--when--i-follow.feature +22 -0
- package/tests/features/test--when--i-go-to-homepage.feature +9 -0
- package/tests/features/test--when--i-move-forward-backward-one-page.feature +14 -0
- package/tests/features/test--when--i-press-button-by-attribute.feature +18 -0
- package/tests/features/test--when--i-press-button.feature +13 -0
- package/tests/features/test--when--i-reload-page.feature +11 -0
- package/tests/features/test--when--i-scroll-left-right.feature +48 -0
- package/tests/features/test--when--i-scroll.feature +48 -0
- package/tests/features/test--when--i-select-option-from-dropdownlist.feature +21 -0
- package/tests/features/test--when--i-select-radio-button.feature +37 -0
- package/tests/features/test--when--i-wait-page-is-loaded.feature +8 -0
- package/tests/features/test--when--i-wait-time.feature +21 -0
- package/tests/features/video.feature +18 -0
- package/tests/features/wait.feature +7 -0
- package/tests/features/yaml.feature +102 -0
- package/tests/selectors/_canonical-keys.json +113 -0
- package/tests/selectors/back-end-selectors.json +19 -0
- package/tests/selectors/cms-contentful-admin.json +35 -0
- package/tests/selectors/cms-craft-admin.json +39 -0
- package/tests/selectors/cms-drupal-cms-gin.json +56 -0
- package/tests/selectors/cms-drupal-core-claro.json +50 -0
- package/tests/selectors/cms-generic-admin.json +34 -0
- package/tests/selectors/cms-ghost-admin.json +34 -0
- package/tests/selectors/cms-joomla-admin.json +41 -0
- package/tests/selectors/cms-magento2-admin.json +32 -0
- package/tests/selectors/cms-prestashop-admin.json +38 -0
- package/tests/selectors/cms-shopify-admin.json +25 -0
- package/tests/selectors/cms-strapi-admin.json +42 -0
- package/tests/selectors/cms-typo3-admin.json +41 -0
- package/tests/selectors/cms-woocommerce-front.json +50 -0
- package/tests/selectors/cms-wordpress-admin.json +31 -0
- package/tests/selectors/framework-ant-design.json +80 -0
- package/tests/selectors/framework-bootstrap.json +86 -0
- package/tests/selectors/framework-bulma.json +74 -0
- package/tests/selectors/framework-chakra.json +58 -0
- package/tests/selectors/framework-foundation.json +51 -0
- package/tests/selectors/framework-material-ui.json +72 -0
- package/tests/selectors/framework-shadcn.json +51 -0
- package/tests/selectors/framework-tailwind.json +40 -0
- package/tests/selectors/framework-vuetify.json +61 -0
- package/tests/selectors/front-end-selectors.json +16 -0
- package/tests/selectors/homepage-selectors.json +13 -0
- package/tests/step-definitions/a11y.steps.js +800 -0
- package/tests/step-definitions/action.steps.js +167 -0
- package/tests/step-definitions/api.steps.js +732 -0
- package/tests/step-definitions/assertion.steps.js +352 -0
- package/tests/step-definitions/auth.steps.js +126 -0
- package/tests/step-definitions/clock.steps.js +128 -0
- package/tests/step-definitions/cookie.steps.js +216 -0
- package/tests/step-definitions/debug.steps.js +39 -0
- package/tests/step-definitions/dialog.steps.js +193 -0
- package/tests/step-definitions/drupal-canvas.steps.js +764 -0
- package/tests/step-definitions/drupal-ckeditor.steps.js +108 -0
- package/tests/step-definitions/drupal-core.steps.js +665 -0
- package/tests/step-definitions/drupal-helpers.js +371 -0
- package/tests/step-definitions/drupal-layout-builder.steps.js +466 -0
- package/tests/step-definitions/drupal-media.steps.js +99 -0
- package/tests/step-definitions/drupal-moderation.steps.js +71 -0
- package/tests/step-definitions/drupal-paragraphs.steps.js +50 -0
- package/tests/step-definitions/element.steps.js +356 -0
- package/tests/step-definitions/field.steps.js +653 -0
- package/tests/step-definitions/file-download.steps.js +211 -0
- package/tests/step-definitions/form.steps.js +333 -0
- package/tests/step-definitions/iframe.steps.js +228 -0
- package/tests/step-definitions/input.steps.js +202 -0
- package/tests/step-definitions/javascript.steps.js +232 -0
- package/tests/step-definitions/keyboard.steps.js +112 -0
- package/tests/step-definitions/link.steps.js +142 -0
- package/tests/step-definitions/metatag.steps.js +91 -0
- package/tests/step-definitions/modal.steps.js +244 -0
- package/tests/step-definitions/navigation.steps.js +218 -0
- package/tests/step-definitions/network.steps.js +280 -0
- package/tests/step-definitions/path.steps.js +141 -0
- package/tests/step-definitions/response.steps.js +98 -0
- package/tests/step-definitions/responsive.steps.js +125 -0
- package/tests/step-definitions/rest.steps.js +111 -0
- package/tests/step-definitions/screenshot.steps.js +423 -0
- package/tests/step-definitions/scroll.steps.js +189 -0
- package/tests/step-definitions/selectors.steps.js +1003 -0
- package/tests/step-definitions/storage.steps.js +194 -0
- package/tests/step-definitions/table.steps.js +181 -0
- package/tests/step-definitions/varbase-e2e.js +897 -0
- package/tests/step-definitions/varbase.steps.js +458 -0
- package/tests/step-definitions/video.steps.js +178 -0
- package/tests/step-definitions/wait.steps.js +400 -0
- package/tests/step-definitions/web-first.steps.js +298 -0
- package/tests/step-definitions/xml.steps.js +411 -0
- package/tests/step-definitions/yaml.steps.js +913 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const reporter = require('cucumber-html-reporter');
|
|
8
|
+
|
|
9
|
+
const THEMES = new Set(['bootstrap', 'hierarchy', 'foundation', 'simple']);
|
|
10
|
+
const FORMATS = new Set(['html', 'pdf', 'all']);
|
|
11
|
+
const PDF_FORMATS = new Set(['Letter', 'Legal', 'Tabloid', 'Ledger', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6']);
|
|
12
|
+
|
|
13
|
+
function capitalize(s) {
|
|
14
|
+
return s ? s.charAt(0).toUpperCase() + s.slice(1) : s;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseArgs(argv) {
|
|
18
|
+
const opts = {
|
|
19
|
+
metadata: {},
|
|
20
|
+
scenarioTimestamp: true,
|
|
21
|
+
reportSuiteAsScenarios: true,
|
|
22
|
+
failedSummaryReport: true,
|
|
23
|
+
launchReport: false,
|
|
24
|
+
noInlineScreenshots: false,
|
|
25
|
+
storeScreenshots: false,
|
|
26
|
+
ignoreBadJsonFile: false,
|
|
27
|
+
formats: [],
|
|
28
|
+
pdfFormat: 'A4',
|
|
29
|
+
pdfLandscape: false,
|
|
30
|
+
pdfMargin: '20mm',
|
|
31
|
+
pdfPrintBackground: true,
|
|
32
|
+
};
|
|
33
|
+
for (let i = 0; i < argv.length; i++) {
|
|
34
|
+
const a = argv[i];
|
|
35
|
+
const next = () => argv[++i];
|
|
36
|
+
switch (a) {
|
|
37
|
+
case '-h':
|
|
38
|
+
case '--help': opts.help = true; break;
|
|
39
|
+
case '--json': opts.jsonFile = next(); break;
|
|
40
|
+
case '--out':
|
|
41
|
+
case '--output': opts.output = next(); break;
|
|
42
|
+
case '--theme': opts.theme = next(); break;
|
|
43
|
+
case '--title': opts.brandTitle = next(); break;
|
|
44
|
+
case '--name': opts.name = next(); break;
|
|
45
|
+
case '--launch': opts.launchReport = true; break;
|
|
46
|
+
case '--layout': opts.columnLayout = Number(next()); break;
|
|
47
|
+
case '--no-scenario-timestamp': opts.scenarioTimestamp = false; break;
|
|
48
|
+
case '--no-suite-as-scenarios': opts.reportSuiteAsScenarios = false; break;
|
|
49
|
+
case '--no-failed-summary': opts.failedSummaryReport = false; break;
|
|
50
|
+
case '--no-inline-screenshots': opts.noInlineScreenshots = true; break;
|
|
51
|
+
case '--store-screenshots': opts.storeScreenshots = true; break;
|
|
52
|
+
case '--screenshots-dir': opts.screenshotsDirectory = next(); break;
|
|
53
|
+
case '--ignore-bad-json': opts.ignoreBadJsonFile = true; break;
|
|
54
|
+
case '--app-version': opts.metadata['App Version'] = next(); break;
|
|
55
|
+
case '--env': opts.metadata['Test Environment'] = next(); break;
|
|
56
|
+
case '--browser': opts.metadata['Browser'] = next(); break;
|
|
57
|
+
case '--platform': opts.metadata['Platform'] = next(); break;
|
|
58
|
+
case '--parallel': opts.metadata['Parallel'] = next(); break;
|
|
59
|
+
case '--executed': opts.metadata['Executed'] = next(); break;
|
|
60
|
+
case '--format': {
|
|
61
|
+
const f = (next() || '').toLowerCase();
|
|
62
|
+
if (!FORMATS.has(f)) {
|
|
63
|
+
console.error(`[varbase-e2e] Unknown format "${f}". Use: ${[...FORMATS].join(', ')}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
if (f === 'all') opts.formats.push('html', 'pdf');
|
|
67
|
+
else opts.formats.push(f);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case '--pdf-out': opts.pdfOut = next(); break;
|
|
71
|
+
case '--pdf-format': opts.pdfFormat = next(); break;
|
|
72
|
+
case '--pdf-landscape': opts.pdfLandscape = true; break;
|
|
73
|
+
case '--pdf-margin': opts.pdfMargin = next(); break;
|
|
74
|
+
case '--pdf-no-background': opts.pdfPrintBackground = false; break;
|
|
75
|
+
case '--pdf-header': opts.pdfHeaderTemplate = next(); break;
|
|
76
|
+
case '--pdf-footer': opts.pdfFooterTemplate = next(); break;
|
|
77
|
+
case '--pdf-scale': opts.pdfScale = Number(next()); break;
|
|
78
|
+
case '--metadata': {
|
|
79
|
+
const kv = next() || '';
|
|
80
|
+
const eq = kv.indexOf('=');
|
|
81
|
+
if (eq > 0) opts.metadata[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
default:
|
|
85
|
+
if (a.startsWith('--')) {
|
|
86
|
+
console.error(`[varbase-e2e] Unknown option: ${a}`);
|
|
87
|
+
opts.help = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (opts.formats.length === 0) opts.formats = ['html'];
|
|
92
|
+
return opts;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function printHelp() {
|
|
96
|
+
process.stdout.write(
|
|
97
|
+
[
|
|
98
|
+
'Usage: generate-reports [options]',
|
|
99
|
+
'',
|
|
100
|
+
'Generate HTML / PDF report from cucumber JSON output.',
|
|
101
|
+
'',
|
|
102
|
+
'Input / Output:',
|
|
103
|
+
' --json <path> JSON input (env VARBASE_E2E_REPORT_JSON,',
|
|
104
|
+
' default: tests/reports/cucumber_report.json)',
|
|
105
|
+
' --out <path> HTML output (env VARBASE_E2E_REPORT_OUT,',
|
|
106
|
+
' default: tests/reports/cucumber_report.html)',
|
|
107
|
+
' --format <kind> html | pdf | all (repeatable)',
|
|
108
|
+
' default: html',
|
|
109
|
+
'',
|
|
110
|
+
'PDF options (require --format pdf or all):',
|
|
111
|
+
' --pdf-out <path> PDF output (default: <out>.pdf)',
|
|
112
|
+
' --pdf-format <name> Letter | Legal | A3 | A4 | A5 | … (default: A4)',
|
|
113
|
+
' --pdf-landscape Landscape orientation',
|
|
114
|
+
' --pdf-margin <css> Margin all sides (default: 20mm)',
|
|
115
|
+
' --pdf-no-background Disable print background colors / images',
|
|
116
|
+
' --pdf-header <html> Header template HTML',
|
|
117
|
+
' --pdf-footer <html> Footer template HTML',
|
|
118
|
+
' --pdf-scale <n> Scale factor 0.1-2.0',
|
|
119
|
+
'',
|
|
120
|
+
'Format:',
|
|
121
|
+
' --theme <name> bootstrap | hierarchy | foundation | simple',
|
|
122
|
+
' (env VARBASE_E2E_REPORT_THEME, default: bootstrap)',
|
|
123
|
+
' --title <s> Brand title (env VARBASE_E2E_REPORT_TITLE)',
|
|
124
|
+
' --name <s> Report name',
|
|
125
|
+
' --layout <1|2> Column layout (default: 1)',
|
|
126
|
+
' --launch Launch report in browser after generation',
|
|
127
|
+
' --no-scenario-timestamp Hide per-scenario timestamp',
|
|
128
|
+
' --no-suite-as-scenarios Report suites instead of scenarios',
|
|
129
|
+
' --no-failed-summary Hide failed-scenarios summary',
|
|
130
|
+
' --no-inline-screenshots Link screenshots instead of embedding',
|
|
131
|
+
' --store-screenshots Copy screenshots into report dir',
|
|
132
|
+
' --screenshots-dir <dir> Source screenshots directory',
|
|
133
|
+
' --ignore-bad-json Do not fail on malformed JSON entries',
|
|
134
|
+
'',
|
|
135
|
+
'Metadata:',
|
|
136
|
+
' --app-version <s> App Version (env VARBASE_E2E_REPORT_APP_VERSION)',
|
|
137
|
+
' --env <s> Test Environment (env VARBASE_E2E_REPORT_ENV)',
|
|
138
|
+
' --browser <s> Browser (env BROWSER)',
|
|
139
|
+
' --platform <s> Platform (default: process.platform)',
|
|
140
|
+
' --parallel <s> Parallel (default: Scenarios)',
|
|
141
|
+
' --executed <s> Executed (env VARBASE_E2E_REPORT_EXECUTED)',
|
|
142
|
+
' --metadata key=value Arbitrary key/value (repeatable)',
|
|
143
|
+
'',
|
|
144
|
+
' -h, --help Show this help',
|
|
145
|
+
'',
|
|
146
|
+
'Examples:',
|
|
147
|
+
' generate-reports',
|
|
148
|
+
' generate-reports --format pdf --pdf-format Letter --pdf-landscape',
|
|
149
|
+
' generate-reports --format all --launch',
|
|
150
|
+
'',
|
|
151
|
+
].join('\n')
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function generatePdf(htmlPath, pdfPath, args) {
|
|
156
|
+
let chromium;
|
|
157
|
+
try {
|
|
158
|
+
({ chromium } = require('playwright'));
|
|
159
|
+
} catch (err) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
'PDF generation requires "playwright". Run `npm install playwright` and try again.\n' +
|
|
162
|
+
`Original error: ${err.message}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (!PDF_FORMATS.has(args.pdfFormat)) {
|
|
166
|
+
throw new Error(`Unknown --pdf-format "${args.pdfFormat}". Use: ${[...PDF_FORMATS].join(', ')}`);
|
|
167
|
+
}
|
|
168
|
+
const browser = await chromium.launch();
|
|
169
|
+
try {
|
|
170
|
+
const context = await browser.newContext();
|
|
171
|
+
const page = await context.newPage();
|
|
172
|
+
const fileUrl = 'file://' + path.resolve(htmlPath);
|
|
173
|
+
// The cucumber-html-reporter output references CDN scripts that may
|
|
174
|
+
// never settle; use `domcontentloaded` and add a short render delay.
|
|
175
|
+
// `commit` returns once navigation accepts; safer than domcontentloaded
|
|
176
|
+
// when the report references third-party CDN assets that may stall.
|
|
177
|
+
await page.goto(fileUrl, { waitUntil: 'commit', timeout: 60000 });
|
|
178
|
+
await page.waitForLoadState('domcontentloaded', { timeout: 30000 }).catch(() => {});
|
|
179
|
+
await page.waitForTimeout(2500);
|
|
180
|
+
|
|
181
|
+
// Render exactly what is shown on screen (preserves badges, panels,
|
|
182
|
+
// gradients). The `print` media flips to greyscale on many themes.
|
|
183
|
+
await page.emulateMedia({ media: 'screen' });
|
|
184
|
+
|
|
185
|
+
// Force-expand every collapsed feature/scenario block and apply
|
|
186
|
+
// print-friendly styles so the PDF shows ALL steps without clipping.
|
|
187
|
+
await page.addStyleTag({ content: `
|
|
188
|
+
/* Preserve every color (status badges, panels, charts) when printing. */
|
|
189
|
+
*, *::before, *::after {
|
|
190
|
+
-webkit-print-color-adjust: exact !important;
|
|
191
|
+
print-color-adjust: exact !important;
|
|
192
|
+
color-adjust: exact !important;
|
|
193
|
+
}
|
|
194
|
+
@media print {
|
|
195
|
+
.collapse, .panel-collapse, [aria-expanded="false"] + .collapse,
|
|
196
|
+
.collapse.in, .collapse.show, .collapsing {
|
|
197
|
+
display: block !important;
|
|
198
|
+
visibility: visible !important;
|
|
199
|
+
height: auto !important;
|
|
200
|
+
max-height: none !important;
|
|
201
|
+
overflow: visible !important;
|
|
202
|
+
opacity: 1 !important;
|
|
203
|
+
}
|
|
204
|
+
.feature, .feature-container, .scenario, .scenario-container,
|
|
205
|
+
.row.steps, .step, .features-summary { page-break-inside: avoid; }
|
|
206
|
+
.feature, .feature-container { page-break-before: auto; }
|
|
207
|
+
details { display: block !important; } details > * { display: block !important; }
|
|
208
|
+
a[href]:after { content: ""; }
|
|
209
|
+
.btn, button.collapse-toggle, .toggle-icon, .glyphicon-chevron-right,
|
|
210
|
+
.glyphicon-chevron-down { display: none !important; }
|
|
211
|
+
pre, code { white-space: pre-wrap !important; word-wrap: break-word !important; }
|
|
212
|
+
img { max-width: 100% !important; height: auto !important; }
|
|
213
|
+
}
|
|
214
|
+
.collapse { display: block !important; height: auto !important; }
|
|
215
|
+
.collapsing { transition: none !important; }
|
|
216
|
+
details:not([open]) { }
|
|
217
|
+
` });
|
|
218
|
+
|
|
219
|
+
// Flatten the report: kill every script, expand every panel, neutralise
|
|
220
|
+
// every collapse trigger so the resulting PDF is static. No JS runs at
|
|
221
|
+
// PDF time — what you see is exactly what gets rendered.
|
|
222
|
+
await page.evaluate(() => {
|
|
223
|
+
document.querySelectorAll('script').forEach(s => s.remove());
|
|
224
|
+
document.querySelectorAll('[data-toggle], [data-bs-toggle]').forEach(el => {
|
|
225
|
+
el.removeAttribute('data-toggle');
|
|
226
|
+
el.removeAttribute('data-bs-toggle');
|
|
227
|
+
el.removeAttribute('data-target');
|
|
228
|
+
el.removeAttribute('data-bs-target');
|
|
229
|
+
el.removeAttribute('href');
|
|
230
|
+
el.style.cursor = 'default';
|
|
231
|
+
});
|
|
232
|
+
document.querySelectorAll('.collapse, .panel-collapse, [class*="collapse"]').forEach(el => {
|
|
233
|
+
el.classList.add('in', 'show');
|
|
234
|
+
el.classList.remove('collapsing');
|
|
235
|
+
el.style.cssText += ';height:auto !important;display:block !important;visibility:visible !important;opacity:1 !important;';
|
|
236
|
+
el.setAttribute('aria-expanded', 'true');
|
|
237
|
+
});
|
|
238
|
+
document.querySelectorAll('[aria-expanded]').forEach(el => el.setAttribute('aria-expanded', 'true'));
|
|
239
|
+
document.querySelectorAll('details').forEach(el => el.setAttribute('open', ''));
|
|
240
|
+
document.querySelectorAll('.glyphicon-chevron-right').forEach(el => {
|
|
241
|
+
el.classList.remove('glyphicon-chevron-right');
|
|
242
|
+
el.classList.add('glyphicon-chevron-down');
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
await page.waitForTimeout(300);
|
|
246
|
+
const opts = {
|
|
247
|
+
path: pdfPath,
|
|
248
|
+
format: args.pdfFormat,
|
|
249
|
+
landscape: !!args.pdfLandscape,
|
|
250
|
+
printBackground: args.pdfPrintBackground,
|
|
251
|
+
margin: {
|
|
252
|
+
top: args.pdfMargin,
|
|
253
|
+
right: args.pdfMargin,
|
|
254
|
+
bottom: args.pdfMargin,
|
|
255
|
+
left: args.pdfMargin,
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
if (args.pdfHeaderTemplate || args.pdfFooterTemplate) {
|
|
259
|
+
opts.displayHeaderFooter = true;
|
|
260
|
+
if (args.pdfHeaderTemplate) opts.headerTemplate = args.pdfHeaderTemplate;
|
|
261
|
+
if (args.pdfFooterTemplate) opts.footerTemplate = args.pdfFooterTemplate;
|
|
262
|
+
}
|
|
263
|
+
if (Number.isFinite(args.pdfScale)) opts.scale = args.pdfScale;
|
|
264
|
+
await page.pdf(opts);
|
|
265
|
+
} finally {
|
|
266
|
+
await browser.close();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function run(argv) {
|
|
271
|
+
const args = parseArgs(argv);
|
|
272
|
+
if (args.help) return printHelp();
|
|
273
|
+
|
|
274
|
+
const cwd = process.cwd();
|
|
275
|
+
const jsonFile = path.resolve(
|
|
276
|
+
cwd,
|
|
277
|
+
args.jsonFile ||
|
|
278
|
+
process.env.VARBASE_E2E_REPORT_JSON ||
|
|
279
|
+
'tests/reports/cucumber_report.json'
|
|
280
|
+
);
|
|
281
|
+
const output = path.resolve(
|
|
282
|
+
cwd,
|
|
283
|
+
args.output ||
|
|
284
|
+
process.env.VARBASE_E2E_REPORT_OUT ||
|
|
285
|
+
'tests/reports/cucumber_report.html'
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
const theme =
|
|
289
|
+
args.theme || process.env.VARBASE_E2E_REPORT_THEME || 'bootstrap';
|
|
290
|
+
if (!THEMES.has(theme)) {
|
|
291
|
+
console.error(
|
|
292
|
+
`[varbase-e2e] Unknown theme "${theme}". Use one of: ${[...THEMES].join(', ')}`
|
|
293
|
+
);
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const metadata = {
|
|
298
|
+
'App Version':
|
|
299
|
+
args.metadata['App Version'] ||
|
|
300
|
+
process.env.VARBASE_E2E_REPORT_APP_VERSION ||
|
|
301
|
+
'2.0.0',
|
|
302
|
+
'Test Environment':
|
|
303
|
+
args.metadata['Test Environment'] ||
|
|
304
|
+
process.env.VARBASE_E2E_REPORT_ENV ||
|
|
305
|
+
'development',
|
|
306
|
+
Browser:
|
|
307
|
+
args.metadata['Browser'] ||
|
|
308
|
+
(process.env.BROWSER ? capitalize(process.env.BROWSER) : 'Chromium'),
|
|
309
|
+
Platform: args.metadata['Platform'] || process.platform,
|
|
310
|
+
Parallel: args.metadata['Parallel'] || 'Scenarios',
|
|
311
|
+
Executed:
|
|
312
|
+
args.metadata['Executed'] ||
|
|
313
|
+
process.env.VARBASE_E2E_REPORT_EXECUTED ||
|
|
314
|
+
'Remote',
|
|
315
|
+
};
|
|
316
|
+
for (const [k, v] of Object.entries(args.metadata)) {
|
|
317
|
+
metadata[k] = v;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const wantHtml = args.formats.includes('html');
|
|
321
|
+
const wantPdf = args.formats.includes('pdf');
|
|
322
|
+
const needsHtmlForExport = wantPdf;
|
|
323
|
+
|
|
324
|
+
const options = {
|
|
325
|
+
theme,
|
|
326
|
+
jsonFile,
|
|
327
|
+
output,
|
|
328
|
+
reportSuiteAsScenarios: args.reportSuiteAsScenarios,
|
|
329
|
+
scenarioTimestamp: args.scenarioTimestamp,
|
|
330
|
+
launchReport: args.launchReport && wantHtml,
|
|
331
|
+
brandTitle:
|
|
332
|
+
args.brandTitle || process.env.VARBASE_E2E_REPORT_TITLE || 'Test Report',
|
|
333
|
+
name: args.name || process.env.VARBASE_E2E_REPORT_NAME,
|
|
334
|
+
columnLayout: args.columnLayout || 1,
|
|
335
|
+
noInlineScreenshots: args.noInlineScreenshots,
|
|
336
|
+
storeScreenshots: args.storeScreenshots,
|
|
337
|
+
screenshotsDirectory: args.screenshotsDirectory,
|
|
338
|
+
ignoreBadJsonFile: args.ignoreBadJsonFile,
|
|
339
|
+
failedSummaryReport: args.failedSummaryReport,
|
|
340
|
+
metadata,
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
if (wantHtml || needsHtmlForExport) {
|
|
344
|
+
reporter.generate(options);
|
|
345
|
+
console.log(`[varbase-e2e] HTML report → ${output}`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (wantPdf) {
|
|
349
|
+
const pdfOut = path.resolve(cwd, args.pdfOut || output.replace(/\.html?$/i, '') + '.pdf');
|
|
350
|
+
await generatePdf(output, pdfOut, args);
|
|
351
|
+
console.log(`[varbase-e2e] PDF report → ${pdfOut}`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (require.main === module) {
|
|
357
|
+
Promise.resolve(run(process.argv.slice(2))).catch(err => {
|
|
358
|
+
console.error(`[varbase-e2e] ${err.message || err}`);
|
|
359
|
+
process.exit(1);
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
module.exports = { run };
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { spawnSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const packagePath = path.join(__dirname, '..');
|
|
10
|
+
|
|
11
|
+
const CUCUMBER_JS = `module.exports = {
|
|
12
|
+
default: {
|
|
13
|
+
timeout: 30000,
|
|
14
|
+
requireModule: ['ts-node/register'],
|
|
15
|
+
require: [
|
|
16
|
+
'node_modules/@vardot/varbase-e2e/tests/step-definitions/**/*.js', // Varbase E2E core step definitions (auto HTML report on exit; disable: VARBASE_E2E_REPORT_DISABLE=1).
|
|
17
|
+
'tests/step-definitions/**/*.js', // Your custom step definitions.
|
|
18
|
+
],
|
|
19
|
+
paths: ['tests/features/**/*.feature'],
|
|
20
|
+
format: [
|
|
21
|
+
'@cucumber/pretty-formatter',
|
|
22
|
+
'json:tests/reports/cucumber_report.json',
|
|
23
|
+
],
|
|
24
|
+
formatOptions: {
|
|
25
|
+
colorsEnabled: true,
|
|
26
|
+
theme: {
|
|
27
|
+
'feature keyword': ['bold', 'blue'],
|
|
28
|
+
'feature name': ['blue', 'underline'],
|
|
29
|
+
'feature description': ['blueBright'],
|
|
30
|
+
'scenario keyword': ['bold', 'magenta'],
|
|
31
|
+
'scenario name': ['magenta', 'underline'],
|
|
32
|
+
'step keyword': ['bold', 'green'],
|
|
33
|
+
'step text': ['greenBright', 'italic'],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
worldParameters: {
|
|
37
|
+
launchUrl: process.env.LAUNCH_URL || 'http://localhost:8080',
|
|
38
|
+
minWaitTime: {
|
|
39
|
+
page: 3000,
|
|
40
|
+
before_scenario: 0,
|
|
41
|
+
after_scenario: 0,
|
|
42
|
+
before_step: 0,
|
|
43
|
+
after_step: 0,
|
|
44
|
+
},
|
|
45
|
+
selectors: {
|
|
46
|
+
css: {},
|
|
47
|
+
xpath: {},
|
|
48
|
+
filesPath: './tests/selectors/',
|
|
49
|
+
files: [],
|
|
50
|
+
offset: 60,
|
|
51
|
+
breakpoints: {
|
|
52
|
+
xs: { width: 375, height: 667 },
|
|
53
|
+
sm: { width: 576, height: 800 },
|
|
54
|
+
md: { width: 768, height: 1024 },
|
|
55
|
+
lg: { width: 992, height: 768 },
|
|
56
|
+
xl: { width: 1200, height: 900, default: true },
|
|
57
|
+
xxl: { width: 1400, height: 900 },
|
|
58
|
+
xxxl: { width: 1920, height: 1080 },
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
screenshot: {
|
|
62
|
+
dir: './screenshots',
|
|
63
|
+
purge: false,
|
|
64
|
+
onFailed: true,
|
|
65
|
+
onEveryStep: false,
|
|
66
|
+
alwaysFullscreen: false,
|
|
67
|
+
failedPrefix: 'failed_',
|
|
68
|
+
filenamePattern: '{datetime}.{feature_file}.feature_{step_line}.{ext}',
|
|
69
|
+
filenamePatternFailed: '{failed_prefix}{datetime}.{feature_file}.feature_{step_line}.{ext}',
|
|
70
|
+
infoTypes: '',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const PLAYWRIGHT_CONFIG_TS = `import type { LaunchOptions, BrowserContextOptions } from 'playwright';
|
|
78
|
+
|
|
79
|
+
type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
|
80
|
+
|
|
81
|
+
const browser = (process.env.BROWSER || 'chromium') as BrowserName;
|
|
82
|
+
|
|
83
|
+
const chromiumArgs: string[] = [
|
|
84
|
+
'--no-sandbox',
|
|
85
|
+
'--disable-dev-shm-usage',
|
|
86
|
+
'--disable-setuid-sandbox',
|
|
87
|
+
'--disable-web-security',
|
|
88
|
+
'--ignore-certificate-errors',
|
|
89
|
+
'--disable-extensions',
|
|
90
|
+
'--incognito',
|
|
91
|
+
'--disable-infobars',
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
interface PlaywrightConfig {
|
|
95
|
+
browser: BrowserName;
|
|
96
|
+
launchOptions: LaunchOptions;
|
|
97
|
+
contextOptions: BrowserContextOptions;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const config: PlaywrightConfig = {
|
|
101
|
+
browser,
|
|
102
|
+
launchOptions: {
|
|
103
|
+
headless: true,
|
|
104
|
+
slowMo: 300,
|
|
105
|
+
args: browser === 'chromium' ? chromiumArgs : [],
|
|
106
|
+
},
|
|
107
|
+
contextOptions: {
|
|
108
|
+
viewport: { width: 1600, height: 1200 },
|
|
109
|
+
ignoreHTTPSErrors: true,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export = config;
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
const TSCONFIG_JSON = {
|
|
117
|
+
compilerOptions: {
|
|
118
|
+
target: 'ES2020',
|
|
119
|
+
module: 'CommonJS',
|
|
120
|
+
strict: true,
|
|
121
|
+
esModuleInterop: true,
|
|
122
|
+
types: ['node'],
|
|
123
|
+
},
|
|
124
|
+
'ts-node': {
|
|
125
|
+
ignore: ['node_modules/(?!@vardot/varbase-e2e/)'],
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const FEATURE_SAMPLE = `Feature: Check the home page
|
|
130
|
+
As a nonymouse user
|
|
131
|
+
I want to be able to visit the home page
|
|
132
|
+
So that I know that the site is working
|
|
133
|
+
|
|
134
|
+
Scenario: Check homepage
|
|
135
|
+
Given I am on the homepage
|
|
136
|
+
Then I should see "Love to help in speeding up the work of having Automated Functional Acceptance Testing for products to ship websites in a swift way."
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
const CUSTOM_STEP_JS = `const { Given, When, Then } = require('@cucumber/cucumber');
|
|
140
|
+
const playwrightConfig = require(require('path').join(process.cwd(), 'playwright.config'));
|
|
141
|
+
|
|
142
|
+
// Add your custom step definitions here.
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
const TEXT_ASSETS = {
|
|
146
|
+
'cucumber.js': CUCUMBER_JS,
|
|
147
|
+
'playwright.config.ts': PLAYWRIGHT_CONFIG_TS,
|
|
148
|
+
'tests/features/README.md': '# Features\n',
|
|
149
|
+
'tests/features/check-homepage.feature': FEATURE_SAMPLE,
|
|
150
|
+
'tests/reports/README.md': '# Reports\n',
|
|
151
|
+
'tests/step-definitions/README.md': '# Step Definitions\n',
|
|
152
|
+
'tests/step-definitions/custom.js': CUSTOM_STEP_JS,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const TEST_SCRIPTS = {
|
|
156
|
+
test: 'cucumber-js --config cucumber.js',
|
|
157
|
+
'test:chromium': 'BROWSER=chromium cucumber-js --config cucumber.js',
|
|
158
|
+
'test:firefox': 'BROWSER=firefox cucumber-js --config cucumber.js',
|
|
159
|
+
'test:webkit': 'BROWSER=webkit cucumber-js --config cucumber.js',
|
|
160
|
+
'generate-reports': 'generate-reports',
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const DEFAULT_PKG = {
|
|
164
|
+
name: 'project-name',
|
|
165
|
+
version: '2.0.0',
|
|
166
|
+
description: '',
|
|
167
|
+
main: 'index.js',
|
|
168
|
+
author: '',
|
|
169
|
+
license: 'MIT',
|
|
170
|
+
private: false,
|
|
171
|
+
scripts: {},
|
|
172
|
+
homepage: 'https://github.com/Vardot/varbase-e2e',
|
|
173
|
+
dependencies: {},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
function parseArgs(argv) {
|
|
177
|
+
const opts = { force: false, skipBrowsers: false, help: false, cwd: null };
|
|
178
|
+
for (let i = 0; i < argv.length; i++) {
|
|
179
|
+
const a = argv[i];
|
|
180
|
+
if (a === '--force' || a === '-f') opts.force = true;
|
|
181
|
+
else if (a === '--skip-browsers') opts.skipBrowsers = true;
|
|
182
|
+
else if (a === '--help' || a === '-h') opts.help = true;
|
|
183
|
+
else if (a === '--cwd') opts.cwd = argv[++i];
|
|
184
|
+
}
|
|
185
|
+
return opts;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function printHelp() {
|
|
189
|
+
process.stdout.write(
|
|
190
|
+
[
|
|
191
|
+
'Usage: init-varbase-e2e [options]',
|
|
192
|
+
'',
|
|
193
|
+
'Scaffold a varbase-e2e project: cucumber.js, playwright.config.ts,',
|
|
194
|
+
'tsconfig.json, tests/ tree, and package.json scripts.',
|
|
195
|
+
'Idempotent — safe to run multiple times.',
|
|
196
|
+
'',
|
|
197
|
+
'Options:',
|
|
198
|
+
' -f, --force Overwrite existing files',
|
|
199
|
+
' --skip-browsers Skip `playwright install chromium`',
|
|
200
|
+
' --cwd <dir> Target directory (default: $INIT_CWD or cwd)',
|
|
201
|
+
' -h, --help Show this help',
|
|
202
|
+
'',
|
|
203
|
+
].join('\n')
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function writeFileIfMissing(destPath, content, { force }) {
|
|
208
|
+
if (!force && fs.existsSync(destPath)) return false;
|
|
209
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
210
|
+
fs.writeFileSync(destPath, content, 'utf8');
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function ensureDir(dir) {
|
|
215
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function readPkgVersion() {
|
|
219
|
+
try {
|
|
220
|
+
const pkg = JSON.parse(
|
|
221
|
+
fs.readFileSync(path.join(packagePath, 'package.json'), 'utf8')
|
|
222
|
+
);
|
|
223
|
+
const major = String(pkg.version).split('.')[0] || '2';
|
|
224
|
+
return `~${major}.0.0`;
|
|
225
|
+
} catch {
|
|
226
|
+
return '~2.0.0';
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function writeTextAssets(projectPath, opts) {
|
|
231
|
+
const results = [];
|
|
232
|
+
for (const [rel, content] of Object.entries(TEXT_ASSETS)) {
|
|
233
|
+
const dest = path.join(projectPath, rel);
|
|
234
|
+
const written = writeFileIfMissing(dest, content, opts);
|
|
235
|
+
results.push({ file: rel, written });
|
|
236
|
+
}
|
|
237
|
+
return results;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function writeTsconfig(projectPath, opts) {
|
|
241
|
+
const dest = path.join(projectPath, 'tsconfig.json');
|
|
242
|
+
if (!opts.force && fs.existsSync(dest)) return false;
|
|
243
|
+
fs.writeFileSync(dest, JSON.stringify(TSCONFIG_JSON, null, 2) + '\n', 'utf8');
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function ensurePackageJson(projectPath, opts) {
|
|
248
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
249
|
+
const existed = fs.existsSync(pkgPath);
|
|
250
|
+
|
|
251
|
+
let pkg;
|
|
252
|
+
if (existed) {
|
|
253
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
254
|
+
} else {
|
|
255
|
+
pkg = { ...DEFAULT_PKG };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
pkg.scripts = pkg.scripts || {};
|
|
259
|
+
for (const [name, cmd] of Object.entries(TEST_SCRIPTS)) {
|
|
260
|
+
if (opts.force || !pkg.scripts[name]) {
|
|
261
|
+
pkg.scripts[name] = cmd;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
266
|
+
if (opts.force || !pkg.dependencies['@vardot/varbase-e2e']) {
|
|
267
|
+
pkg.dependencies['@vardot/varbase-e2e'] = readPkgVersion();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
271
|
+
return { created: !existed };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function installBrowsers(projectPath) {
|
|
275
|
+
console.log('\n [varbase-e2e] Installing Playwright browsers...\n');
|
|
276
|
+
const res = spawnSync(
|
|
277
|
+
'npx',
|
|
278
|
+
['playwright', 'install', '--with-deps', 'chromium'],
|
|
279
|
+
{ cwd: projectPath, stdio: 'inherit', shell: true }
|
|
280
|
+
);
|
|
281
|
+
return res.status === 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function run(argv) {
|
|
285
|
+
const opts = parseArgs(argv);
|
|
286
|
+
if (opts.help) return printHelp();
|
|
287
|
+
|
|
288
|
+
const projectPath = path.resolve(
|
|
289
|
+
opts.cwd || process.env.INIT_CWD || process.cwd()
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
if (
|
|
293
|
+
projectPath === packagePath ||
|
|
294
|
+
projectPath.startsWith(path.join(packagePath, 'node_modules'))
|
|
295
|
+
) {
|
|
296
|
+
console.log(' [varbase-e2e] Skipping init inside varbase-e2e itself.');
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.log(`\n [varbase-e2e] Initializing project at ${projectPath}\n`);
|
|
301
|
+
|
|
302
|
+
ensureDir(projectPath);
|
|
303
|
+
ensureDir(path.join(projectPath, 'tests', 'features'));
|
|
304
|
+
ensureDir(path.join(projectPath, 'tests', 'reports'));
|
|
305
|
+
ensureDir(path.join(projectPath, 'tests', 'step-definitions'));
|
|
306
|
+
ensureDir(path.join(projectPath, 'tests', 'selectors'));
|
|
307
|
+
ensureDir(path.join(projectPath, 'screenshots'));
|
|
308
|
+
|
|
309
|
+
const assetResults = writeTextAssets(projectPath, opts);
|
|
310
|
+
const tsconfigWritten = writeTsconfig(projectPath, opts);
|
|
311
|
+
const pkgResult = ensurePackageJson(projectPath, opts);
|
|
312
|
+
|
|
313
|
+
for (const r of assetResults) {
|
|
314
|
+
console.log(` [${r.written ? 'wrote ' : 'kept '}] ${r.file}`);
|
|
315
|
+
}
|
|
316
|
+
console.log(` [${tsconfigWritten ? 'wrote ' : 'kept '}] tsconfig.json`);
|
|
317
|
+
console.log(
|
|
318
|
+
` [${pkgResult.created ? 'wrote ' : 'merged'}] package.json`
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
if (!opts.skipBrowsers) installBrowsers(projectPath);
|
|
322
|
+
|
|
323
|
+
console.log('\n [varbase-e2e] Setup complete!\n');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (require.main === module) {
|
|
327
|
+
run(process.argv.slice(2));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
module.exports = { run };
|