@nextsparkjs/ai-workflow 0.1.0-beta.100
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/LICENSE +21 -0
- package/README.md +115 -0
- package/claude/_docs/workflows-optimizations.md +359 -0
- package/claude/agents/api-tester.md +634 -0
- package/claude/agents/architecture-supervisor.md +1351 -0
- package/claude/agents/backend-developer.md +997 -0
- package/claude/agents/backend-validator.md +417 -0
- package/claude/agents/bdd-docs-writer.md +737 -0
- package/claude/agents/block-developer.md +677 -0
- package/claude/agents/code-reviewer.md +1432 -0
- package/claude/agents/db-developer.md +721 -0
- package/claude/agents/db-validator.md +407 -0
- package/claude/agents/demo-video-generator.md +493 -0
- package/claude/agents/documentation-writer.md +1268 -0
- package/claude/agents/frontend-developer.md +1234 -0
- package/claude/agents/frontend-validator.md +777 -0
- package/claude/agents/functional-validator.md +630 -0
- package/claude/agents/mock-analyst.md +387 -0
- package/claude/agents/product-manager.md +963 -0
- package/claude/agents/qa-automation.md +1762 -0
- package/claude/agents/release-manager.md +634 -0
- package/claude/agents/selectors-translator.md +262 -0
- package/claude/agents/unit-test-writer.md +785 -0
- package/claude/agents/visual-comparator.md +329 -0
- package/claude/agents/workflow-maintainer.md +352 -0
- package/claude/commands/do/README.md +88 -0
- package/claude/commands/do/create-api.md +64 -0
- package/claude/commands/do/create-entity.md +66 -0
- package/claude/commands/do/create-migration.md +64 -0
- package/claude/commands/do/create-plugin.md +56 -0
- package/claude/commands/do/create-theme.md +70 -0
- package/claude/commands/do/mock-data.md +67 -0
- package/claude/commands/do/reset-db.md +71 -0
- package/claude/commands/do/setup-scheduled-action.md +75 -0
- package/claude/commands/do/sync-code-review.md +117 -0
- package/claude/commands/do/update-selectors.md +112 -0
- package/claude/commands/do/use-skills.md +90 -0
- package/claude/commands/do/validate-blocks.md +69 -0
- package/claude/commands/how-to/README.md +261 -0
- package/claude/commands/how-to/add-metadata.md +692 -0
- package/claude/commands/how-to/add-taxonomies.md +806 -0
- package/claude/commands/how-to/add-translations.md +571 -0
- package/claude/commands/how-to/create-api.md +577 -0
- package/claude/commands/how-to/create-block.md +575 -0
- package/claude/commands/how-to/create-child-entities.md +771 -0
- package/claude/commands/how-to/create-entity.md +597 -0
- package/claude/commands/how-to/create-migrations.md +605 -0
- package/claude/commands/how-to/create-plugin.md +654 -0
- package/claude/commands/how-to/customize-app.md +481 -0
- package/claude/commands/how-to/customize-dashboard.md +553 -0
- package/claude/commands/how-to/customize-theme.md +438 -0
- package/claude/commands/how-to/define-features-flows.md +632 -0
- package/claude/commands/how-to/deploy.md +507 -0
- package/claude/commands/how-to/handle-file-uploads.md +746 -0
- package/claude/commands/how-to/implement-search.md +1001 -0
- package/claude/commands/how-to/install-plugins.md +352 -0
- package/claude/commands/how-to/manage-test-coverage.md +984 -0
- package/claude/commands/how-to/run-tests.md +400 -0
- package/claude/commands/how-to/set-app-languages.md +601 -0
- package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
- package/claude/commands/how-to/set-scheduled-actions.md +527 -0
- package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
- package/claude/commands/how-to/setup-authentication.md +388 -0
- package/claude/commands/how-to/setup-claude-code.md +440 -0
- package/claude/commands/how-to/setup-database.md +274 -0
- package/claude/commands/how-to/setup-email-providers.md +598 -0
- package/claude/commands/how-to/setup-mobile-dev.md +627 -0
- package/claude/commands/how-to/start.md +500 -0
- package/claude/commands/how-to/use-devtools.md +639 -0
- package/claude/commands/how-to/use-superadmin.md +622 -0
- package/claude/commands/session/README.md +193 -0
- package/claude/commands/session/block-create.md +190 -0
- package/claude/commands/session/block-list.md +203 -0
- package/claude/commands/session/block-update.md +192 -0
- package/claude/commands/session/block-validate.md +218 -0
- package/claude/commands/session/changelog.md +115 -0
- package/claude/commands/session/close.md +225 -0
- package/claude/commands/session/commit.md +174 -0
- package/claude/commands/session/db-entity.md +206 -0
- package/claude/commands/session/db-fix.md +212 -0
- package/claude/commands/session/db-sample.md +206 -0
- package/claude/commands/session/demo.md +178 -0
- package/claude/commands/session/doc-bdd.md +207 -0
- package/claude/commands/session/doc-feature.md +218 -0
- package/claude/commands/session/doc-read.md +225 -0
- package/claude/commands/session/execute.md +204 -0
- package/claude/commands/session/explain.md +202 -0
- package/claude/commands/session/fix-bug.md +210 -0
- package/claude/commands/session/fix-build.md +182 -0
- package/claude/commands/session/fix-test.md +189 -0
- package/claude/commands/session/pending.md +232 -0
- package/claude/commands/session/refine.md +188 -0
- package/claude/commands/session/resume.md +192 -0
- package/claude/commands/session/review.md +192 -0
- package/claude/commands/session/scope-change.md +181 -0
- package/claude/commands/session/start-blocks.md +347 -0
- package/claude/commands/session/start.md +604 -0
- package/claude/commands/session/status.md +169 -0
- package/claude/commands/session/test-fix.md +221 -0
- package/claude/commands/session/test-run.md +203 -0
- package/claude/commands/session/test-write.md +242 -0
- package/claude/commands/session/validate.md +162 -0
- package/claude/config/context.json +40 -0
- package/claude/config/github.json +69 -0
- package/claude/config/github.schema.json +106 -0
- package/claude/config/team.json +46 -0
- package/claude/config/team.schema.json +106 -0
- package/claude/config/workspace.json +43 -0
- package/claude/config/workspace.schema.json +75 -0
- package/claude/skills/README.md +228 -0
- package/claude/skills/accessibility/SKILL.md +573 -0
- package/claude/skills/api-bypass-layers/SKILL.md +550 -0
- package/claude/skills/asana-integration/SKILL.md +499 -0
- package/claude/skills/better-auth/SKILL.md +666 -0
- package/claude/skills/billing-subscriptions/SKILL.md +660 -0
- package/claude/skills/block-decision-matrix/SKILL.md +359 -0
- package/claude/skills/clickup-integration/SKILL.md +434 -0
- package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
- package/claude/skills/create-plugin/SKILL.md +425 -0
- package/claude/skills/create-theme/SKILL.md +331 -0
- package/claude/skills/cypress-api/SKILL.md +511 -0
- package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
- package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
- package/claude/skills/cypress-e2e/SKILL.md +526 -0
- package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
- package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
- package/claude/skills/cypress-selectors/SKILL.md +309 -0
- package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
- package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
- package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
- package/claude/skills/database-migrations/SKILL.md +335 -0
- package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
- package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
- package/claude/skills/design-system/SKILL.md +682 -0
- package/claude/skills/documentation/SKILL.md +540 -0
- package/claude/skills/entity-api/SKILL.md +482 -0
- package/claude/skills/entity-system/SKILL.md +635 -0
- package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
- package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
- package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
- package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
- package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
- package/claude/skills/github/SKILL.md +467 -0
- package/claude/skills/i18n-nextintl/SKILL.md +302 -0
- package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
- package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
- package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
- package/claude/skills/impact-analysis/SKILL.md +203 -0
- package/claude/skills/jest-unit/SKILL.md +306 -0
- package/claude/skills/jest-unit/references/component-testing.md +371 -0
- package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
- package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
- package/claude/skills/jira-integration/SKILL.md +539 -0
- package/claude/skills/media-library/SKILL.md +743 -0
- package/claude/skills/mock-analysis/SKILL.md +276 -0
- package/claude/skills/monorepo-architecture/SKILL.md +162 -0
- package/claude/skills/nextjs-api-development/SKILL.md +364 -0
- package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
- package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
- package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
- package/claude/skills/notion-integration/SKILL.md +641 -0
- package/claude/skills/npm-development-workflow/SKILL.md +480 -0
- package/claude/skills/page-builder-blocks/SKILL.md +530 -0
- package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
- package/claude/skills/permissions-system/SKILL.md +619 -0
- package/claude/skills/plugins/SKILL.md +340 -0
- package/claude/skills/plugins/references/plugin-templates.md +414 -0
- package/claude/skills/plugins/references/plugin-testing.md +353 -0
- package/claude/skills/plugins/references/plugin-types.md +198 -0
- package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
- package/claude/skills/pom-patterns/SKILL.md +452 -0
- package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
- package/claude/skills/rate-limiting/SKILL.md +342 -0
- package/claude/skills/react-best-practices/AGENTS.md +2410 -0
- package/claude/skills/react-best-practices/README.md +123 -0
- package/claude/skills/react-best-practices/SKILL.md +125 -0
- package/claude/skills/react-best-practices/metadata.json +15 -0
- package/claude/skills/react-best-practices/rules/_sections.md +46 -0
- package/claude/skills/react-best-practices/rules/_template.md +28 -0
- package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/claude/skills/react-patterns/SKILL.md +688 -0
- package/claude/skills/registry-system/SKILL.md +331 -0
- package/claude/skills/scheduled-actions/SKILL.md +671 -0
- package/claude/skills/scope-enforcement/SKILL.md +542 -0
- package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
- package/claude/skills/server-actions/SKILL.md +493 -0
- package/claude/skills/service-layer/SKILL.md +587 -0
- package/claude/skills/session-management/SKILL.md +266 -0
- package/claude/skills/session-management/scripts/create-session.py +166 -0
- package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
- package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
- package/claude/skills/session-management/scripts/session-archive.sh +87 -0
- package/claude/skills/session-management/scripts/session-close.sh +133 -0
- package/claude/skills/session-management/scripts/session-init.sh +225 -0
- package/claude/skills/session-management/scripts/session-list.sh +163 -0
- package/claude/skills/session-management/scripts/split-plan.sh +116 -0
- package/claude/skills/shadcn-components/SKILL.md +586 -0
- package/claude/skills/shadcn-theming/SKILL.md +446 -0
- package/claude/skills/suspense-loading/SKILL.md +280 -0
- package/claude/skills/tailwind-theming/SKILL.md +507 -0
- package/claude/skills/tanstack-query/SKILL.md +608 -0
- package/claude/skills/test-coverage/SKILL.md +239 -0
- package/claude/skills/web-design-guidelines/SKILL.md +39 -0
- package/claude/skills/zod-validation/SKILL.md +537 -0
- package/claude/templates/blocks/progress.md +86 -0
- package/claude/templates/iteration/changes.md +61 -0
- package/claude/templates/iteration/progress.md +55 -0
- package/claude/templates/log.md +31 -0
- package/claude/templates/story/context.md +77 -0
- package/claude/templates/story/pendings.md +37 -0
- package/claude/templates/story/plan.md +299 -0
- package/claude/templates/story/requirements.md +109 -0
- package/claude/templates/story/scope.json +10 -0
- package/claude/templates/story/tests.md +91 -0
- package/claude/templates/task/progress.md +58 -0
- package/claude/templates/task/requirements.md +54 -0
- package/claude/workflows/README.md +154 -0
- package/claude/workflows/blocks.md +614 -0
- package/claude/workflows/story.md +1207 -0
- package/claude/workflows/task.md +927 -0
- package/claude/workflows/tweak.md +527 -0
- package/cursor/.gitkeep +0 -0
- package/package.json +35 -0
- package/scripts/postinstall.mjs +198 -0
- package/scripts/setup.mjs +282 -0
- package/scripts/sync.mjs +209 -0
|
@@ -0,0 +1,788 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Generate UAT Test Script
|
|
4
|
+
|
|
5
|
+
Generates a UAT test file and optional BDD documentation for an entity.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python generate-uat-test.py --entity ENTITY --role ROLE [--theme THEME] [--with-bdd]
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--entity ENTITY Entity name (e.g., tasks, customers)
|
|
12
|
+
--theme THEME Theme name (default: default)
|
|
13
|
+
--role ROLE Role name (owner, admin, member, editor, viewer)
|
|
14
|
+
--session SESSION Session name for @scope tag (optional)
|
|
15
|
+
--with-bdd Generate BDD documentation file
|
|
16
|
+
--dry-run Preview without writing to file
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
import re
|
|
22
|
+
import argparse
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
from typing import Optional
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def to_pascal_case(name: str) -> str:
|
|
29
|
+
"""Convert kebab-case/snake_case to PascalCase."""
|
|
30
|
+
return ''.join(x.title() for x in re.split(r'[-_]', name))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def to_singular(name: str) -> str:
|
|
34
|
+
"""Convert plural to singular (simple English rules)."""
|
|
35
|
+
if name.endswith('ies'):
|
|
36
|
+
return name[:-3] + 'y'
|
|
37
|
+
elif name.endswith('es'):
|
|
38
|
+
return name[:-2]
|
|
39
|
+
elif name.endswith('s'):
|
|
40
|
+
return name[:-1]
|
|
41
|
+
return name
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_login_helper(role: str) -> str:
|
|
45
|
+
"""Get the login helper function name for a role."""
|
|
46
|
+
role_map = {
|
|
47
|
+
'owner': 'loginAsOwner',
|
|
48
|
+
'admin': 'loginAsAdmin',
|
|
49
|
+
'member': 'loginAsMember',
|
|
50
|
+
'editor': 'loginAsEditor',
|
|
51
|
+
'viewer': 'loginAsViewer',
|
|
52
|
+
'superadmin': 'loginAsSuperadmin',
|
|
53
|
+
'developer': 'loginAsDeveloper',
|
|
54
|
+
}
|
|
55
|
+
return role_map.get(role.lower(), 'loginAsOwner')
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_role_access_level(role: str) -> str:
|
|
59
|
+
"""Get access level description for a role."""
|
|
60
|
+
access_map = {
|
|
61
|
+
'owner': 'Full CRUD Access',
|
|
62
|
+
'admin': 'Full CRUD Access',
|
|
63
|
+
'member': 'Limited Access',
|
|
64
|
+
'editor': 'Edit Access',
|
|
65
|
+
'viewer': 'Read-Only Access',
|
|
66
|
+
}
|
|
67
|
+
return access_map.get(role.lower(), 'Standard Access')
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def generate_test_content(
|
|
71
|
+
entity: str,
|
|
72
|
+
role: str,
|
|
73
|
+
theme: str,
|
|
74
|
+
session: Optional[str] = None
|
|
75
|
+
) -> str:
|
|
76
|
+
"""Generate the UAT test file content."""
|
|
77
|
+
singular = to_singular(entity)
|
|
78
|
+
pascal_singular = to_pascal_case(singular)
|
|
79
|
+
pascal_plural = to_pascal_case(entity)
|
|
80
|
+
role_upper = role.upper()
|
|
81
|
+
entity_upper = entity.upper()
|
|
82
|
+
login_helper = get_login_helper(role)
|
|
83
|
+
access_level = get_role_access_level(role)
|
|
84
|
+
timestamp = datetime.now().strftime('%Y-%m-%d')
|
|
85
|
+
|
|
86
|
+
# Build tags
|
|
87
|
+
tags = [f"'@uat'", f"'@feat-{entity}'", f"'@role-{role}'", "'@crud'", "'@regression'"]
|
|
88
|
+
if session:
|
|
89
|
+
tags.append(f"'@scope-{session}'")
|
|
90
|
+
tags_str = ', '.join(tags)
|
|
91
|
+
|
|
92
|
+
return f'''/// <reference types="cypress" />
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* {pascal_plural} CRUD - {role.title()} Role ({access_level})
|
|
96
|
+
*
|
|
97
|
+
* Generated: {timestamp}
|
|
98
|
+
* Theme: {theme}
|
|
99
|
+
*
|
|
100
|
+
* UAT tests for {entity} entity with {role.title()} role permissions.
|
|
101
|
+
*/
|
|
102
|
+
|
|
103
|
+
import * as allure from 'allure-cypress'
|
|
104
|
+
|
|
105
|
+
import {{ {pascal_plural}POM }} from '../../src/entities/{pascal_plural}POM'
|
|
106
|
+
import {{ {login_helper} }} from '../../src/session-helpers'
|
|
107
|
+
|
|
108
|
+
describe('{pascal_plural} CRUD - {role.title()} Role ({access_level})', {{
|
|
109
|
+
tags: [{tags_str}]
|
|
110
|
+
}}, () => {{
|
|
111
|
+
const pom = {pascal_plural}POM.create()
|
|
112
|
+
|
|
113
|
+
beforeEach(() => {{
|
|
114
|
+
allure.epic('UAT')
|
|
115
|
+
allure.feature('{pascal_plural}')
|
|
116
|
+
allure.story('{role.title()} Permissions')
|
|
117
|
+
pom.setupApiIntercepts()
|
|
118
|
+
{login_helper}()
|
|
119
|
+
pom.visitList()
|
|
120
|
+
pom.api.waitForList()
|
|
121
|
+
pom.waitForList()
|
|
122
|
+
}})
|
|
123
|
+
|
|
124
|
+
// ============================================================
|
|
125
|
+
// CREATE - {role.title()} can create {entity}
|
|
126
|
+
// ============================================================
|
|
127
|
+
describe('CREATE - {role.title()} can create {entity}', {{ tags: '@smoke' }}, () => {{
|
|
128
|
+
it('{role_upper}_{entity_upper}_CREATE_001: should create new {singular} successfully', {{ tags: '@smoke' }}, () => {{
|
|
129
|
+
allure.severity('critical')
|
|
130
|
+
|
|
131
|
+
const entityName = `{role.title()} {pascal_singular} ${{Date.now()}}`
|
|
132
|
+
|
|
133
|
+
// Click create button
|
|
134
|
+
pom.clickAdd()
|
|
135
|
+
|
|
136
|
+
// Validate form is visible
|
|
137
|
+
pom.waitForForm()
|
|
138
|
+
|
|
139
|
+
// TODO: Fill required fields based on entity schema
|
|
140
|
+
// pom.fillTextField('title', entityName)
|
|
141
|
+
|
|
142
|
+
// Submit form
|
|
143
|
+
pom.submitForm()
|
|
144
|
+
|
|
145
|
+
// Wait for API response
|
|
146
|
+
pom.api.waitForCreate()
|
|
147
|
+
|
|
148
|
+
// Validate redirect to list
|
|
149
|
+
cy.url().should('include', `/dashboard/${{pom.entitySlug}}`)
|
|
150
|
+
|
|
151
|
+
// Validate entity appears in list
|
|
152
|
+
pom.assert{pascal_singular}InList(entityName)
|
|
153
|
+
|
|
154
|
+
cy.log('Created {singular} successfully')
|
|
155
|
+
}})
|
|
156
|
+
|
|
157
|
+
it('{role_upper}_{entity_upper}_CREATE_002: should create {singular} with all fields', () => {{
|
|
158
|
+
const entityName = `Full {pascal_singular} ${{Date.now()}}`
|
|
159
|
+
|
|
160
|
+
// Click create button
|
|
161
|
+
pom.clickAdd()
|
|
162
|
+
|
|
163
|
+
// Validate form is visible
|
|
164
|
+
pom.waitForForm()
|
|
165
|
+
|
|
166
|
+
// TODO: Fill all fields based on entity schema
|
|
167
|
+
// pom.fillTextField('title', entityName)
|
|
168
|
+
// pom.fillTextarea('description', 'Description text')
|
|
169
|
+
// pom.selectOption('status', 'active')
|
|
170
|
+
|
|
171
|
+
// Submit form
|
|
172
|
+
pom.submitForm()
|
|
173
|
+
|
|
174
|
+
// Wait for API response
|
|
175
|
+
pom.api.waitForCreate()
|
|
176
|
+
|
|
177
|
+
// Validate entity appears in list
|
|
178
|
+
cy.url().should('include', `/dashboard/${{pom.entitySlug}}`)
|
|
179
|
+
pom.assert{pascal_singular}InList(entityName)
|
|
180
|
+
|
|
181
|
+
cy.log('Created full {singular} successfully')
|
|
182
|
+
}})
|
|
183
|
+
}})
|
|
184
|
+
|
|
185
|
+
// ============================================================
|
|
186
|
+
// READ - {role.title()} can read {entity}
|
|
187
|
+
// ============================================================
|
|
188
|
+
describe('READ - {role.title()} can read {entity}', {{ tags: '@smoke' }}, () => {{
|
|
189
|
+
it('{role_upper}_{entity_upper}_READ_001: should view {singular} list', {{ tags: '@smoke' }}, () => {{
|
|
190
|
+
allure.severity('critical')
|
|
191
|
+
// Validate list is visible
|
|
192
|
+
pom.assertTableVisible()
|
|
193
|
+
|
|
194
|
+
cy.log('{role.title()} can view {singular} list')
|
|
195
|
+
}})
|
|
196
|
+
|
|
197
|
+
it('{role_upper}_{entity_upper}_READ_002: should view {singular} details', () => {{
|
|
198
|
+
// Check if there are entities to view
|
|
199
|
+
cy.get('body').then($body => {{
|
|
200
|
+
if ($body.find(pom.selectors.rowGeneric).length > 0) {{
|
|
201
|
+
// Click the first row (navigation via onClick)
|
|
202
|
+
cy.get(pom.selectors.rowGeneric).first().click()
|
|
203
|
+
|
|
204
|
+
// Should navigate to detail page
|
|
205
|
+
cy.url().should('match', new RegExp(`/dashboard/${{pom.entitySlug}}/[a-z0-9-]+`))
|
|
206
|
+
|
|
207
|
+
cy.log('{role.title()} can view {singular} details')
|
|
208
|
+
}} else {{
|
|
209
|
+
cy.log('No {entity} available to view details')
|
|
210
|
+
}}
|
|
211
|
+
}})
|
|
212
|
+
}})
|
|
213
|
+
}})
|
|
214
|
+
|
|
215
|
+
// ============================================================
|
|
216
|
+
// UPDATE - {role.title()} can update {entity}
|
|
217
|
+
// ============================================================
|
|
218
|
+
describe('UPDATE - {role.title()} can update {entity}', () => {{
|
|
219
|
+
it('{role_upper}_{entity_upper}_UPDATE_001: should edit {singular} successfully', () => {{
|
|
220
|
+
// Check if there are entities to edit
|
|
221
|
+
cy.get('body').then($body => {{
|
|
222
|
+
if ($body.find(pom.selectors.rowActionEditGeneric).length > 0) {{
|
|
223
|
+
// Click edit button on first entity
|
|
224
|
+
cy.get(pom.selectors.rowActionEditGeneric).first().click()
|
|
225
|
+
|
|
226
|
+
// Validate form is visible
|
|
227
|
+
pom.waitForForm()
|
|
228
|
+
|
|
229
|
+
// TODO: Update entity-specific fields
|
|
230
|
+
const updatedName = `Updated {pascal_singular} ${{Date.now()}}`
|
|
231
|
+
// pom.fillTextField('title', updatedName)
|
|
232
|
+
|
|
233
|
+
// Submit form
|
|
234
|
+
pom.submitForm()
|
|
235
|
+
|
|
236
|
+
// Wait for API response
|
|
237
|
+
pom.api.waitForUpdate()
|
|
238
|
+
|
|
239
|
+
// Validate update
|
|
240
|
+
cy.url().should('include', `/dashboard/${{pom.entitySlug}}`)
|
|
241
|
+
|
|
242
|
+
cy.log('{role.title()} updated {singular} successfully')
|
|
243
|
+
}} else {{
|
|
244
|
+
cy.log('No {entity} available to edit')
|
|
245
|
+
}}
|
|
246
|
+
}})
|
|
247
|
+
}})
|
|
248
|
+
}})
|
|
249
|
+
|
|
250
|
+
// ============================================================
|
|
251
|
+
// DELETE - {role.title()} can delete {entity}
|
|
252
|
+
// ============================================================
|
|
253
|
+
describe('DELETE - {role.title()} can delete {entity}', () => {{
|
|
254
|
+
it('{role_upper}_{entity_upper}_DELETE_001: should delete {singular} successfully', () => {{
|
|
255
|
+
// First, create an entity to delete
|
|
256
|
+
const entityName = `Delete Test ${{Date.now()}}`
|
|
257
|
+
|
|
258
|
+
pom.clickAdd()
|
|
259
|
+
pom.waitForForm()
|
|
260
|
+
// TODO: Fill required fields
|
|
261
|
+
// pom.fillTextField('title', entityName)
|
|
262
|
+
pom.submitForm()
|
|
263
|
+
pom.api.waitForCreate()
|
|
264
|
+
|
|
265
|
+
// Navigate back to list and wait for it to load
|
|
266
|
+
pom.visitList()
|
|
267
|
+
pom.waitForList()
|
|
268
|
+
|
|
269
|
+
// Wait for entity to appear in the list
|
|
270
|
+
pom.assert{pascal_singular}InList(entityName)
|
|
271
|
+
|
|
272
|
+
// Delete the entity
|
|
273
|
+
pom.clickRowByText(entityName)
|
|
274
|
+
pom.waitForDetail()
|
|
275
|
+
|
|
276
|
+
// Click delete button
|
|
277
|
+
pom.clickDelete()
|
|
278
|
+
|
|
279
|
+
// Confirm deletion (2-step)
|
|
280
|
+
cy.get(pom.selectors.deleteConfirm).should('be.visible').click()
|
|
281
|
+
cy.get(pom.selectors.parentDeleteConfirm).should('be.visible').click()
|
|
282
|
+
|
|
283
|
+
// Wait for delete API response
|
|
284
|
+
pom.api.waitForDelete()
|
|
285
|
+
|
|
286
|
+
// Navigate to list and verify deletion
|
|
287
|
+
pom.visitList()
|
|
288
|
+
pom.api.waitForList()
|
|
289
|
+
pom.waitForList()
|
|
290
|
+
pom.assert{pascal_singular}NotInList(entityName)
|
|
291
|
+
|
|
292
|
+
cy.log('{role.title()} deleted {singular} successfully')
|
|
293
|
+
}})
|
|
294
|
+
}})
|
|
295
|
+
|
|
296
|
+
after(() => {{
|
|
297
|
+
cy.log('{pascal_plural} CRUD tests completed')
|
|
298
|
+
}})
|
|
299
|
+
}})
|
|
300
|
+
'''
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def generate_bdd_content(
|
|
304
|
+
entity: str,
|
|
305
|
+
role: str,
|
|
306
|
+
theme: str
|
|
307
|
+
) -> str:
|
|
308
|
+
"""Generate the BDD documentation file content."""
|
|
309
|
+
singular = to_singular(entity)
|
|
310
|
+
singular_es = singular # Would need proper translation
|
|
311
|
+
pascal_singular = to_pascal_case(singular)
|
|
312
|
+
pascal_plural = to_pascal_case(entity)
|
|
313
|
+
role_upper = role.upper()
|
|
314
|
+
entity_upper = entity.upper()
|
|
315
|
+
access_level = get_role_access_level(role)
|
|
316
|
+
|
|
317
|
+
# Spanish translations (simplified)
|
|
318
|
+
role_es = {
|
|
319
|
+
'owner': 'Owner',
|
|
320
|
+
'admin': 'Admin',
|
|
321
|
+
'member': 'Member',
|
|
322
|
+
'editor': 'Editor',
|
|
323
|
+
'viewer': 'Viewer',
|
|
324
|
+
}.get(role.lower(), role.title())
|
|
325
|
+
|
|
326
|
+
return f'''# {pascal_plural} UI - {role.title()} Role (Format: BDD/Gherkin - Bilingual)
|
|
327
|
+
|
|
328
|
+
> **Test File:** `{entity}-{role}.cy.ts`
|
|
329
|
+
> **Format:** Behavior-Driven Development (BDD) with Given/When/Then
|
|
330
|
+
> **Languages:** English / Spanish (side-by-side)
|
|
331
|
+
> **Total Tests:** 6
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Feature: {pascal_plural} Management - {role.title()} Role ({access_level})
|
|
336
|
+
|
|
337
|
+
<table>
|
|
338
|
+
<tr>
|
|
339
|
+
<th width="50%">English</th>
|
|
340
|
+
<th width="50%">Espanol</th>
|
|
341
|
+
</tr>
|
|
342
|
+
<tr>
|
|
343
|
+
<td>
|
|
344
|
+
|
|
345
|
+
As a **{role.title()}**
|
|
346
|
+
I want to **manage {entity} through the dashboard UI**
|
|
347
|
+
So that **I can create, read, update, and delete {entity} for my team**
|
|
348
|
+
|
|
349
|
+
</td>
|
|
350
|
+
<td>
|
|
351
|
+
|
|
352
|
+
Como **{role_es}**
|
|
353
|
+
Quiero **gestionar {entity} a traves del dashboard**
|
|
354
|
+
Para **crear, leer, actualizar y eliminar {entity} de mi equipo**
|
|
355
|
+
|
|
356
|
+
</td>
|
|
357
|
+
</tr>
|
|
358
|
+
</table>
|
|
359
|
+
|
|
360
|
+
### Background
|
|
361
|
+
|
|
362
|
+
<table>
|
|
363
|
+
<tr>
|
|
364
|
+
<th width="50%">English</th>
|
|
365
|
+
<th width="50%">Espanol</th>
|
|
366
|
+
</tr>
|
|
367
|
+
<tr>
|
|
368
|
+
<td>
|
|
369
|
+
|
|
370
|
+
```gherkin
|
|
371
|
+
Given I am logged in as {role.title()}
|
|
372
|
+
And I have navigated to the {pascal_plural} dashboard
|
|
373
|
+
And the {entity} list has loaded successfully
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
</td>
|
|
377
|
+
<td>
|
|
378
|
+
|
|
379
|
+
```gherkin
|
|
380
|
+
Given estoy logueado como {role_es}
|
|
381
|
+
And he navegado al dashboard de {pascal_plural}
|
|
382
|
+
And la lista de {entity} ha cargado exitosamente
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
</td>
|
|
386
|
+
</tr>
|
|
387
|
+
</table>
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## CREATE - {role.title()} can create {entity} `@smoke`
|
|
392
|
+
|
|
393
|
+
### {role_upper}_{entity_upper}_CREATE_001: Create new {singular} successfully `@smoke` `@critical`
|
|
394
|
+
|
|
395
|
+
<table>
|
|
396
|
+
<tr>
|
|
397
|
+
<th width="50%">English</th>
|
|
398
|
+
<th width="50%">Espanol</th>
|
|
399
|
+
</tr>
|
|
400
|
+
<tr>
|
|
401
|
+
<td>
|
|
402
|
+
|
|
403
|
+
```gherkin
|
|
404
|
+
Scenario: {role.title()} creates a simple {singular}
|
|
405
|
+
|
|
406
|
+
Given I am logged in as {role.title()}
|
|
407
|
+
And I am on the {pascal_plural} list page
|
|
408
|
+
When I click the "Add" button
|
|
409
|
+
Then the {singular} creation form should appear
|
|
410
|
+
|
|
411
|
+
When I enter "New {pascal_singular}" in the Title field
|
|
412
|
+
And I click the "Save" button
|
|
413
|
+
Then the form should submit successfully
|
|
414
|
+
And I should see a success message
|
|
415
|
+
And I should be redirected to the {entity} list
|
|
416
|
+
And I should see "New {pascal_singular}" in the {singular} list
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
</td>
|
|
420
|
+
<td>
|
|
421
|
+
|
|
422
|
+
```gherkin
|
|
423
|
+
Scenario: {role_es} crea un/a {singular_es} simple
|
|
424
|
+
|
|
425
|
+
Given estoy logueado como {role_es}
|
|
426
|
+
And estoy en la pagina de lista de {pascal_plural}
|
|
427
|
+
When hago clic en el boton "Agregar"
|
|
428
|
+
Then deberia aparecer el formulario de creacion
|
|
429
|
+
|
|
430
|
+
When ingreso "Nuevo/a {pascal_singular}" en el campo Titulo
|
|
431
|
+
And hago clic en el boton "Guardar"
|
|
432
|
+
Then el formulario deberia enviarse exitosamente
|
|
433
|
+
And deberia ver un mensaje de exito
|
|
434
|
+
And deberia ser redirigido a la lista de {entity}
|
|
435
|
+
And deberia ver "Nuevo/a {pascal_singular}" en la lista
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
</td>
|
|
439
|
+
</tr>
|
|
440
|
+
</table>
|
|
441
|
+
|
|
442
|
+
**Visual Flow:**
|
|
443
|
+
```
|
|
444
|
+
[{pascal_plural} List] -> [Click Add] -> [Form Modal] -> [Fill Title] -> [Save] -> [List with new {singular}]
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
### {role_upper}_{entity_upper}_CREATE_002: Create {singular} with all fields
|
|
450
|
+
|
|
451
|
+
<table>
|
|
452
|
+
<tr>
|
|
453
|
+
<th width="50%">English</th>
|
|
454
|
+
<th width="50%">Espanol</th>
|
|
455
|
+
</tr>
|
|
456
|
+
<tr>
|
|
457
|
+
<td>
|
|
458
|
+
|
|
459
|
+
```gherkin
|
|
460
|
+
Scenario: {role.title()} creates a {singular} with complete information
|
|
461
|
+
|
|
462
|
+
Given I am logged in as {role.title()}
|
|
463
|
+
And I am on the {pascal_plural} list page
|
|
464
|
+
When I click the "Add" button
|
|
465
|
+
Then the {singular} creation form should appear
|
|
466
|
+
|
|
467
|
+
When I fill in all available fields
|
|
468
|
+
And I click the "Save" button
|
|
469
|
+
Then the form should submit successfully
|
|
470
|
+
And the {singular} should be created with all provided values
|
|
471
|
+
And I should see the {singular} in the list
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
</td>
|
|
475
|
+
<td>
|
|
476
|
+
|
|
477
|
+
```gherkin
|
|
478
|
+
Scenario: {role_es} crea un/a {singular_es} con informacion completa
|
|
479
|
+
|
|
480
|
+
Given estoy logueado como {role_es}
|
|
481
|
+
And estoy en la pagina de lista de {pascal_plural}
|
|
482
|
+
When hago clic en el boton "Agregar"
|
|
483
|
+
Then deberia aparecer el formulario de creacion
|
|
484
|
+
|
|
485
|
+
When completo todos los campos disponibles
|
|
486
|
+
And hago clic en el boton "Guardar"
|
|
487
|
+
Then el formulario deberia enviarse exitosamente
|
|
488
|
+
And el/la {singular_es} deberia crearse con todos los valores
|
|
489
|
+
And deberia ver el/la {singular_es} en la lista
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
</td>
|
|
493
|
+
</tr>
|
|
494
|
+
</table>
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## READ - {role.title()} can read {entity} `@smoke`
|
|
499
|
+
|
|
500
|
+
### {role_upper}_{entity_upper}_READ_001: View {singular} list `@smoke` `@critical`
|
|
501
|
+
|
|
502
|
+
<table>
|
|
503
|
+
<tr>
|
|
504
|
+
<th width="50%">English</th>
|
|
505
|
+
<th width="50%">Espanol</th>
|
|
506
|
+
</tr>
|
|
507
|
+
<tr>
|
|
508
|
+
<td>
|
|
509
|
+
|
|
510
|
+
```gherkin
|
|
511
|
+
Scenario: {role.title()} can view the {entity} table
|
|
512
|
+
|
|
513
|
+
Given I am logged in as {role.title()}
|
|
514
|
+
When I navigate to the {pascal_plural} dashboard
|
|
515
|
+
Then I should see the {entity} table
|
|
516
|
+
And the table should display {singular} information
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
</td>
|
|
520
|
+
<td>
|
|
521
|
+
|
|
522
|
+
```gherkin
|
|
523
|
+
Scenario: {role_es} puede ver la tabla de {entity}
|
|
524
|
+
|
|
525
|
+
Given estoy logueado como {role_es}
|
|
526
|
+
When navego al dashboard de {pascal_plural}
|
|
527
|
+
Then deberia ver la tabla de {entity}
|
|
528
|
+
And la tabla deberia mostrar informacion de {singular_es}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
</td>
|
|
532
|
+
</tr>
|
|
533
|
+
</table>
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
### {role_upper}_{entity_upper}_READ_002: View {singular} details
|
|
538
|
+
|
|
539
|
+
<table>
|
|
540
|
+
<tr>
|
|
541
|
+
<th width="50%">English</th>
|
|
542
|
+
<th width="50%">Espanol</th>
|
|
543
|
+
</tr>
|
|
544
|
+
<tr>
|
|
545
|
+
<td>
|
|
546
|
+
|
|
547
|
+
```gherkin
|
|
548
|
+
Scenario: {role.title()} can view individual {singular} details
|
|
549
|
+
|
|
550
|
+
Given I am logged in as {role.title()}
|
|
551
|
+
And there is at least one {singular} in the list
|
|
552
|
+
When I click on a {singular} row
|
|
553
|
+
Then I should be navigated to the {singular} detail page
|
|
554
|
+
And the URL should contain the {singular} ID
|
|
555
|
+
And I should see the complete {singular} information
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
</td>
|
|
559
|
+
<td>
|
|
560
|
+
|
|
561
|
+
```gherkin
|
|
562
|
+
Scenario: {role_es} puede ver detalles de un/a {singular_es}
|
|
563
|
+
|
|
564
|
+
Given estoy logueado como {role_es}
|
|
565
|
+
And existe al menos un/a {singular_es} en la lista
|
|
566
|
+
When hago clic en una fila de {singular_es}
|
|
567
|
+
Then deberia ser navegado a la pagina de detalle
|
|
568
|
+
And la URL deberia contener el ID del/la {singular_es}
|
|
569
|
+
And deberia ver la informacion completa
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
</td>
|
|
573
|
+
</tr>
|
|
574
|
+
</table>
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## UPDATE - {role.title()} can update {entity}
|
|
579
|
+
|
|
580
|
+
### {role_upper}_{entity_upper}_UPDATE_001: Edit existing {singular}
|
|
581
|
+
|
|
582
|
+
<table>
|
|
583
|
+
<tr>
|
|
584
|
+
<th width="50%">English</th>
|
|
585
|
+
<th width="50%">Espanol</th>
|
|
586
|
+
</tr>
|
|
587
|
+
<tr>
|
|
588
|
+
<td>
|
|
589
|
+
|
|
590
|
+
```gherkin
|
|
591
|
+
Scenario: {role.title()} can modify {singular} information
|
|
592
|
+
|
|
593
|
+
Given I am logged in as {role.title()}
|
|
594
|
+
And there is at least one {singular} in the list
|
|
595
|
+
When I click the "Edit" button on a {singular} row
|
|
596
|
+
Then the {singular} edit form should appear
|
|
597
|
+
And the form should be pre-filled with current {singular} data
|
|
598
|
+
|
|
599
|
+
When I change the Title to "Updated {pascal_singular}"
|
|
600
|
+
And I click the "Save" button
|
|
601
|
+
Then the changes should be saved successfully
|
|
602
|
+
And I should be redirected to the {entity} list
|
|
603
|
+
And I should see "Updated {pascal_singular}" in the list
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
</td>
|
|
607
|
+
<td>
|
|
608
|
+
|
|
609
|
+
```gherkin
|
|
610
|
+
Scenario: {role_es} puede modificar informacion de {singular_es}
|
|
611
|
+
|
|
612
|
+
Given estoy logueado como {role_es}
|
|
613
|
+
And existe al menos un/a {singular_es} en la lista
|
|
614
|
+
When hago clic en el boton "Editar" de una fila
|
|
615
|
+
Then deberia aparecer el formulario de edicion
|
|
616
|
+
And el formulario deberia estar pre-llenado con datos actuales
|
|
617
|
+
|
|
618
|
+
When cambio el Titulo a "Actualizado/a {pascal_singular}"
|
|
619
|
+
And hago clic en el boton "Guardar"
|
|
620
|
+
Then los cambios deberian guardarse exitosamente
|
|
621
|
+
And deberia ser redirigido a la lista de {entity}
|
|
622
|
+
And deberia ver "Actualizado/a {pascal_singular}" en la lista
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
</td>
|
|
626
|
+
</tr>
|
|
627
|
+
</table>
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## DELETE - {role.title()} can delete {entity}
|
|
632
|
+
|
|
633
|
+
### {role_upper}_{entity_upper}_DELETE_001: Delete existing {singular} `@critical`
|
|
634
|
+
|
|
635
|
+
<table>
|
|
636
|
+
<tr>
|
|
637
|
+
<th width="50%">English</th>
|
|
638
|
+
<th width="50%">Espanol</th>
|
|
639
|
+
</tr>
|
|
640
|
+
<tr>
|
|
641
|
+
<td>
|
|
642
|
+
|
|
643
|
+
```gherkin
|
|
644
|
+
Scenario: {role.title()} can permanently delete a {singular}
|
|
645
|
+
|
|
646
|
+
Given I am logged in as {role.title()}
|
|
647
|
+
And I have created a {singular} called "{pascal_singular} to Delete"
|
|
648
|
+
When I click on the {singular} to view details
|
|
649
|
+
And I click the "Delete" button
|
|
650
|
+
Then a confirmation dialog should appear
|
|
651
|
+
|
|
652
|
+
When I confirm the deletion
|
|
653
|
+
Then a second confirmation dialog should appear
|
|
654
|
+
When I confirm again
|
|
655
|
+
Then the {singular} should be deleted
|
|
656
|
+
And I should be redirected to the {entity} list
|
|
657
|
+
And "{pascal_singular} to Delete" should no longer appear in the list
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
</td>
|
|
661
|
+
<td>
|
|
662
|
+
|
|
663
|
+
```gherkin
|
|
664
|
+
Scenario: {role_es} puede eliminar permanentemente un/a {singular_es}
|
|
665
|
+
|
|
666
|
+
Given estoy logueado como {role_es}
|
|
667
|
+
And he creado un/a {singular_es} llamado/a "{pascal_singular} a Eliminar"
|
|
668
|
+
When hago clic en el/la {singular_es} para ver detalles
|
|
669
|
+
And hago clic en el boton "Eliminar"
|
|
670
|
+
Then deberia aparecer un dialogo de confirmacion
|
|
671
|
+
|
|
672
|
+
When confirmo la eliminacion
|
|
673
|
+
Then deberia aparecer un segundo dialogo de confirmacion
|
|
674
|
+
When confirmo nuevamente
|
|
675
|
+
Then el/la {singular_es} deberia ser eliminado/a
|
|
676
|
+
And deberia ser redirigido a la lista de {entity}
|
|
677
|
+
And "{pascal_singular} a Eliminar" ya no deberia aparecer en la lista
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
</td>
|
|
681
|
+
</tr>
|
|
682
|
+
</table>
|
|
683
|
+
|
|
684
|
+
**Deletion Flow (2-step confirmation):**
|
|
685
|
+
```
|
|
686
|
+
[Detail Page] -> [Delete Button] -> [Confirm Dialog 1] -> [Confirm Dialog 2] -> [{pascal_singular} Deleted]
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
> **Note:** The double confirmation is a safety feature to prevent accidental deletions.
|
|
690
|
+
|
|
691
|
+
---
|
|
692
|
+
|
|
693
|
+
## Summary
|
|
694
|
+
|
|
695
|
+
| Test ID | Operation | Description | Tags |
|
|
696
|
+
|---------|-----------|-------------|------|
|
|
697
|
+
| {role_upper}_{entity_upper}_CREATE_001 | CREATE | Create {singular} with title | `@smoke` `@critical` |
|
|
698
|
+
| {role_upper}_{entity_upper}_CREATE_002 | CREATE | Create {singular} with all fields | |
|
|
699
|
+
| {role_upper}_{entity_upper}_READ_001 | READ | View {singular} list | `@smoke` `@critical` |
|
|
700
|
+
| {role_upper}_{entity_upper}_READ_002 | READ | View {singular} details | |
|
|
701
|
+
| {role_upper}_{entity_upper}_UPDATE_001 | UPDATE | Edit existing {singular} | |
|
|
702
|
+
| {role_upper}_{entity_upper}_DELETE_001 | DELETE | Delete {singular} | `@critical` |
|
|
703
|
+
'''
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def main():
|
|
707
|
+
parser = argparse.ArgumentParser(description='Generate UAT test file')
|
|
708
|
+
parser.add_argument('--entity', required=True, help='Entity name (e.g., tasks)')
|
|
709
|
+
parser.add_argument('--theme', default='default', help='Theme name')
|
|
710
|
+
parser.add_argument('--role', required=True, help='Role name (owner, admin, member, editor, viewer)')
|
|
711
|
+
parser.add_argument('--session', default=None, help='Session name for @scope tag')
|
|
712
|
+
parser.add_argument('--with-bdd', action='store_true', help='Generate BDD documentation')
|
|
713
|
+
parser.add_argument('--dry-run', action='store_true', help='Preview without writing')
|
|
714
|
+
parser.add_argument('--output', default=None, help='Output directory path')
|
|
715
|
+
|
|
716
|
+
args = parser.parse_args()
|
|
717
|
+
|
|
718
|
+
entity = args.entity.lower()
|
|
719
|
+
role = args.role.lower()
|
|
720
|
+
theme = args.theme
|
|
721
|
+
|
|
722
|
+
print(f"\n{'=' * 60}")
|
|
723
|
+
print("GENERATING UAT TEST")
|
|
724
|
+
print(f"{'=' * 60}")
|
|
725
|
+
print(f"Entity: {entity}")
|
|
726
|
+
print(f"Role: {role}")
|
|
727
|
+
print(f"Theme: {theme}")
|
|
728
|
+
print(f"Session: {args.session or '(none)'}")
|
|
729
|
+
print(f"With BDD: {args.with_bdd}")
|
|
730
|
+
print(f"{'=' * 60}\n")
|
|
731
|
+
|
|
732
|
+
# Generate test content
|
|
733
|
+
test_content = generate_test_content(entity, role, theme, args.session)
|
|
734
|
+
|
|
735
|
+
# Determine output paths
|
|
736
|
+
if args.output:
|
|
737
|
+
output_dir = Path(args.output)
|
|
738
|
+
else:
|
|
739
|
+
output_dir = Path(f'contents/themes/{theme}/tests/cypress/e2e/uat/{entity}')
|
|
740
|
+
|
|
741
|
+
test_file = output_dir / f'{entity}-{role}.cy.ts'
|
|
742
|
+
bdd_file = output_dir / f'{entity}-{role}.bdd.md'
|
|
743
|
+
|
|
744
|
+
if args.dry_run:
|
|
745
|
+
print("DRY RUN - Generated test content:\n")
|
|
746
|
+
print("-" * 60)
|
|
747
|
+
print(test_content[:2000] + "\n... (truncated)")
|
|
748
|
+
print("-" * 60)
|
|
749
|
+
|
|
750
|
+
if args.with_bdd:
|
|
751
|
+
bdd_content = generate_bdd_content(entity, role, theme)
|
|
752
|
+
print("\nDRY RUN - Generated BDD content:\n")
|
|
753
|
+
print("-" * 60)
|
|
754
|
+
print(bdd_content[:2000] + "\n... (truncated)")
|
|
755
|
+
print("-" * 60)
|
|
756
|
+
|
|
757
|
+
print("\nRun without --dry-run to write files.")
|
|
758
|
+
return 0
|
|
759
|
+
|
|
760
|
+
# Create directories
|
|
761
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
762
|
+
|
|
763
|
+
# Write test file
|
|
764
|
+
with open(test_file, 'w', encoding='utf-8') as f:
|
|
765
|
+
f.write(test_content)
|
|
766
|
+
print(f"Test file generated: {test_file}")
|
|
767
|
+
|
|
768
|
+
# Write BDD file if requested
|
|
769
|
+
if args.with_bdd:
|
|
770
|
+
bdd_content = generate_bdd_content(entity, role, theme)
|
|
771
|
+
with open(bdd_file, 'w', encoding='utf-8') as f:
|
|
772
|
+
f.write(bdd_content)
|
|
773
|
+
print(f"BDD file generated: {bdd_file}")
|
|
774
|
+
|
|
775
|
+
print(f"\n{'=' * 60}")
|
|
776
|
+
print("NEXT STEPS:")
|
|
777
|
+
print("=" * 60)
|
|
778
|
+
print(f"1. Review generated file(s)")
|
|
779
|
+
print(f"2. Customize TODO sections based on entity schema")
|
|
780
|
+
print(f"3. Ensure {to_pascal_case(entity)}POM exists in src/entities/")
|
|
781
|
+
print(f"4. Run tests: pnpm cy:run --spec \"{test_file}\"")
|
|
782
|
+
print("=" * 60 + "\n")
|
|
783
|
+
|
|
784
|
+
return 0
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
if __name__ == '__main__':
|
|
788
|
+
sys.exit(main())
|