@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,309 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cypress-selectors
|
|
3
|
+
description: |
|
|
4
|
+
3-level selector system for testing: CORE_SELECTORS + BLOCK_SELECTORS + THEME_SELECTORS.
|
|
5
|
+
Provides helpers sel(), cySelector(), entitySelectors() for components and POMs.
|
|
6
|
+
Use this skill when working with data-cy attributes, POMs, or Cypress tests.
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash(python:*)
|
|
8
|
+
version: 1.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Cypress Selectors System
|
|
12
|
+
|
|
13
|
+
Centralized `data-cy` selector system for Cypress testing.
|
|
14
|
+
|
|
15
|
+
## Architecture Overview
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────────────────────────────┐
|
|
19
|
+
│ CORE (Read-Only) │
|
|
20
|
+
│ core/lib/test/core-selectors.ts │
|
|
21
|
+
│ core/lib/test/selector-factory.ts │
|
|
22
|
+
└─────────────────┬───────────────────────┘
|
|
23
|
+
│ imports
|
|
24
|
+
▼
|
|
25
|
+
┌─────────────────────────────────────────┐
|
|
26
|
+
│ THEME (Editable) │
|
|
27
|
+
│ contents/themes/{theme}/lib/selectors.ts│
|
|
28
|
+
│ ├── BLOCK_SELECTORS │
|
|
29
|
+
│ ├── DEVTOOLS_SELECTORS │
|
|
30
|
+
│ ├── THEME_SELECTORS = { │
|
|
31
|
+
│ │ ...CORE_SELECTORS, │
|
|
32
|
+
│ │ blocks: BLOCK_SELECTORS, │
|
|
33
|
+
│ │ devtools: DEVTOOLS_SELECTORS │
|
|
34
|
+
│ │ } │
|
|
35
|
+
│ └── exports: sel, cySelector, etc. │
|
|
36
|
+
└─────────────────┬───────────────────────┘
|
|
37
|
+
│ re-exports
|
|
38
|
+
▼
|
|
39
|
+
┌─────────────────────────────────────────┐
|
|
40
|
+
│ tests/cypress/src/selectors.ts │
|
|
41
|
+
│ (re-exports for test files) │
|
|
42
|
+
└─────────────────┬───────────────────────┘
|
|
43
|
+
│ imports
|
|
44
|
+
┌─────────┴─────────┐
|
|
45
|
+
▼ ▼
|
|
46
|
+
┌───────────────┐ ┌───────────────┐
|
|
47
|
+
│ Components │ │ POMs │
|
|
48
|
+
│ sel('x.y') │ │ cySelector() │
|
|
49
|
+
└───────────────┘ └───────────────┘
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## When to Use This Skill
|
|
53
|
+
|
|
54
|
+
- Adding `data-cy` attributes to components
|
|
55
|
+
- Creating new BLOCK_SELECTORS for a block
|
|
56
|
+
- Working with Page Object Models (POMs)
|
|
57
|
+
- Validating selector coverage in UI
|
|
58
|
+
- Writing Cypress E2E tests
|
|
59
|
+
|
|
60
|
+
## Key Functions
|
|
61
|
+
|
|
62
|
+
### `sel(path: string, replacements?: object)`
|
|
63
|
+
|
|
64
|
+
Use in **components** to get the selector string for `data-cy`:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// In block component: contents/themes/{theme}/blocks/{block-name}/component.tsx
|
|
68
|
+
import { sel } from '../../lib/selectors'
|
|
69
|
+
|
|
70
|
+
// Static selector - note the 'blocks.' prefix for block selectors
|
|
71
|
+
<section data-cy={sel('blocks.hero.container')}>
|
|
72
|
+
<h2 data-cy={sel('blocks.hero.title')}>{title}</h2>
|
|
73
|
+
</section>
|
|
74
|
+
|
|
75
|
+
// Dynamic selector with replacement
|
|
76
|
+
{items.map((item, index) => (
|
|
77
|
+
<div data-cy={sel('blocks.faqAccordion.item', { index: String(index) })}>
|
|
78
|
+
{item.content}
|
|
79
|
+
</div>
|
|
80
|
+
))}
|
|
81
|
+
|
|
82
|
+
// Core selectors (no 'blocks.' prefix)
|
|
83
|
+
<button data-cy={sel('auth.login.submit')}>Login</button>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `cySelector(path: string, replacements?: object)`
|
|
87
|
+
|
|
88
|
+
Use in **POMs and Cypress tests** to get the CSS selector `[data-cy="..."]`:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// In POM: contents/themes/{theme}/tests/cypress/src/features/MyPOM.ts
|
|
92
|
+
import { BasePOM } from '../core/BasePOM'
|
|
93
|
+
import { cySelector } from '../selectors'
|
|
94
|
+
|
|
95
|
+
export class HeroPOM extends BasePOM {
|
|
96
|
+
get selectors() {
|
|
97
|
+
return {
|
|
98
|
+
container: cySelector('blocks.hero.container'),
|
|
99
|
+
title: cySelector('blocks.hero.title'),
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Dynamic selectors
|
|
104
|
+
getItem(index: number) {
|
|
105
|
+
return cy.get(cySelector('blocks.faqAccordion.item', { index: String(index) }))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Factory method
|
|
109
|
+
static create(): HeroPOM {
|
|
110
|
+
return new HeroPOM()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// In test file: contents/themes/{theme}/tests/cypress/e2e/blocks/hero.cy.ts
|
|
117
|
+
import { cySelector } from '../../src/selectors'
|
|
118
|
+
|
|
119
|
+
describe('Hero Block', () => {
|
|
120
|
+
it('should display the hero', () => {
|
|
121
|
+
cy.get(cySelector('blocks.hero.container')).should('be.visible')
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `entitySelectors(slug: string)`
|
|
127
|
+
|
|
128
|
+
Get all selectors for a specific entity:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { entitySelectors } from '../../src/selectors'
|
|
132
|
+
|
|
133
|
+
const taskSelectors = entitySelectors('tasks')
|
|
134
|
+
// Returns: { table, row, createBtn, editBtn, ... }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Selector Paths
|
|
138
|
+
|
|
139
|
+
**IMPORTANT:** Block selectors use the `blocks.` prefix because BLOCK_SELECTORS is nested under `blocks:` in THEME_SELECTORS:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// Block selectors - use 'blocks.' prefix
|
|
143
|
+
sel('blocks.hero.container') // → 'block-hero'
|
|
144
|
+
sel('blocks.faqAccordion.item', { index: '0' }) // → 'faq-item-0'
|
|
145
|
+
sel('blocks.pricingTable.plan', { index: '1' }) // → 'pricing-plan-1'
|
|
146
|
+
|
|
147
|
+
// Core selectors - no prefix
|
|
148
|
+
sel('auth.login.form') // → 'login-form'
|
|
149
|
+
sel('entity.table', { slug: 'tasks' }) // → 'tasks-table'
|
|
150
|
+
|
|
151
|
+
// Devtools selectors - use 'devtools.' prefix
|
|
152
|
+
sel('devtools.scheduledActions.table') // → 'scheduled-actions-table'
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Naming Conventions
|
|
156
|
+
|
|
157
|
+
### Static Selectors
|
|
158
|
+
```
|
|
159
|
+
domain-element
|
|
160
|
+
```
|
|
161
|
+
Examples:
|
|
162
|
+
- `block-hero` (block container)
|
|
163
|
+
- `login-form` (core component)
|
|
164
|
+
- `nav-main` (navigation element)
|
|
165
|
+
|
|
166
|
+
### Dynamic Selectors with Placeholders
|
|
167
|
+
```
|
|
168
|
+
{placeholder}-element-{placeholder}
|
|
169
|
+
```
|
|
170
|
+
Placeholders:
|
|
171
|
+
- `{index}` - Array index (most common for blocks)
|
|
172
|
+
- `{slug}` - Entity slug (tasks, customers, pages)
|
|
173
|
+
- `{id}` - Record ID
|
|
174
|
+
- `{name}` - Field name
|
|
175
|
+
- `{action}` - Action name (edit, delete)
|
|
176
|
+
|
|
177
|
+
Examples:
|
|
178
|
+
- `faq-item-{index}` → `faq-item-0`
|
|
179
|
+
- `{slug}-row-{id}` → `tasks-row-123`
|
|
180
|
+
- `field-{name}` → `field-email`
|
|
181
|
+
|
|
182
|
+
## File Locations
|
|
183
|
+
|
|
184
|
+
| Type | Location | Who Modifies |
|
|
185
|
+
|------|----------|--------------|
|
|
186
|
+
| Core selectors | `core/lib/test/core-selectors.ts` | Core maintainers only |
|
|
187
|
+
| Selector factory | `core/lib/test/selector-factory.ts` | Core maintainers only |
|
|
188
|
+
| Block selectors | `contents/themes/{theme}/lib/selectors.ts` | Theme developers |
|
|
189
|
+
| Test re-exports | `contents/themes/{theme}/tests/cypress/src/selectors.ts` | Auto (re-exports) |
|
|
190
|
+
| POMs | `contents/themes/{theme}/tests/cypress/src/features/*POM.ts` | Test developers |
|
|
191
|
+
|
|
192
|
+
## Adding New Selectors
|
|
193
|
+
|
|
194
|
+
### For Block Components
|
|
195
|
+
|
|
196
|
+
Edit `contents/themes/{theme}/lib/selectors.ts`, add to BLOCK_SELECTORS:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
export const BLOCK_SELECTORS = {
|
|
200
|
+
// ... existing blocks
|
|
201
|
+
myNewBlock: {
|
|
202
|
+
container: 'block-my-new-block',
|
|
203
|
+
title: 'my-block-title',
|
|
204
|
+
item: 'my-block-item-{index}',
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Then use in component with `blocks.` prefix:
|
|
210
|
+
```tsx
|
|
211
|
+
// contents/themes/{theme}/blocks/my-new-block/component.tsx
|
|
212
|
+
import { sel } from '../../lib/selectors'
|
|
213
|
+
|
|
214
|
+
<section data-cy={sel('blocks.myNewBlock.container')}>
|
|
215
|
+
<h2 data-cy={sel('blocks.myNewBlock.title')}>{title}</h2>
|
|
216
|
+
</section>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### For Core Components
|
|
220
|
+
|
|
221
|
+
Edit `core/lib/test/core-selectors.ts`:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
export const CORE_SELECTORS = {
|
|
225
|
+
// ... existing selectors
|
|
226
|
+
myNewFeature: {
|
|
227
|
+
container: 'my-feature-container',
|
|
228
|
+
list: 'my-feature-list',
|
|
229
|
+
item: 'my-feature-item-{id}',
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### For DevTools Components
|
|
235
|
+
|
|
236
|
+
Edit `contents/themes/{theme}/lib/selectors.ts`, add to DEVTOOLS_SELECTORS:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
export const DEVTOOLS_SELECTORS = {
|
|
240
|
+
// ... existing selectors
|
|
241
|
+
myDevTool: {
|
|
242
|
+
page: 'devtools-my-tool-page',
|
|
243
|
+
table: 'my-tool-table',
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Anti-Patterns
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// ❌ NEVER: Hardcoded data-cy in component
|
|
252
|
+
<button data-cy="submit-btn">
|
|
253
|
+
|
|
254
|
+
// ❌ NEVER: Direct selector string in test
|
|
255
|
+
cy.get('[data-cy="submit-btn"]')
|
|
256
|
+
|
|
257
|
+
// ❌ NEVER: Missing 'blocks.' prefix for block selectors
|
|
258
|
+
sel('hero.container') // Wrong!
|
|
259
|
+
sel('blocks.hero.container') // Correct!
|
|
260
|
+
|
|
261
|
+
// ❌ NEVER: Import from core in theme components
|
|
262
|
+
import { sel } from '@/core/lib/test' // Wrong!
|
|
263
|
+
import { sel } from '../../lib/selectors' // Correct!
|
|
264
|
+
|
|
265
|
+
// ❌ NEVER: Add selector without UI element
|
|
266
|
+
// Only add selectors for existing UI elements
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Validation Scripts
|
|
270
|
+
|
|
271
|
+
### Validate all selectors use sel()
|
|
272
|
+
```bash
|
|
273
|
+
python .claude/skills/cypress-selectors/scripts/validate-selectors.py
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Find elements missing data-cy
|
|
277
|
+
```bash
|
|
278
|
+
python .claude/skills/cypress-selectors/scripts/extract-missing.py --path contents/themes/default/blocks/
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Generate block selectors for new block
|
|
282
|
+
```bash
|
|
283
|
+
# Basic generation
|
|
284
|
+
python .claude/skills/cypress-selectors/scripts/generate-block-selectors.py --block my-new-block
|
|
285
|
+
|
|
286
|
+
# Analyze existing block and generate full example
|
|
287
|
+
python .claude/skills/cypress-selectors/scripts/generate-block-selectors.py --block faq-accordion --analyze --full
|
|
288
|
+
|
|
289
|
+
# Just show selector entry without instructions
|
|
290
|
+
python .claude/skills/cypress-selectors/scripts/generate-block-selectors.py --block my-block --dry-run
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Checklist
|
|
294
|
+
|
|
295
|
+
Before committing UI changes:
|
|
296
|
+
|
|
297
|
+
- [ ] All interactive elements have `data-cy={sel('...')}`
|
|
298
|
+
- [ ] Block selectors use `sel('blocks.{blockName}.{element}')` path
|
|
299
|
+
- [ ] New selectors added to BLOCK_SELECTORS or CORE_SELECTORS
|
|
300
|
+
- [ ] POM updated with new selectors using `cySelector()`
|
|
301
|
+
- [ ] Selector validation test created in `_selectors/*.cy.ts`
|
|
302
|
+
- [ ] No hardcoded `data-cy` strings in components
|
|
303
|
+
- [ ] No direct core imports in theme files
|
|
304
|
+
|
|
305
|
+
## References
|
|
306
|
+
|
|
307
|
+
- Load `references/architecture.md` for detailed architecture
|
|
308
|
+
- Load `references/naming-conventions.md` for naming rules
|
|
309
|
+
- Load `references/anti-patterns.md` for common mistakes
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Extract Missing Selectors Script
|
|
4
|
+
|
|
5
|
+
Finds interactive elements (buttons, inputs, links, forms) that are missing
|
|
6
|
+
data-cy attributes.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python extract-missing.py --path PATH [--output OUTPUT]
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--path PATH Directory to scan
|
|
13
|
+
--output OUTPUT Output file for results (default: stdout)
|
|
14
|
+
--format FORMAT Output format: text, json, markdown (default: text)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
import json
|
|
21
|
+
import argparse
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import List, Dict, Set
|
|
24
|
+
|
|
25
|
+
# Interactive elements that should have data-cy
|
|
26
|
+
INTERACTIVE_ELEMENTS = [
|
|
27
|
+
'button',
|
|
28
|
+
'Button',
|
|
29
|
+
'input',
|
|
30
|
+
'Input',
|
|
31
|
+
'select',
|
|
32
|
+
'Select',
|
|
33
|
+
'textarea',
|
|
34
|
+
'Textarea',
|
|
35
|
+
'form',
|
|
36
|
+
'Form',
|
|
37
|
+
'a',
|
|
38
|
+
'Link',
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# Pattern to find JSX elements
|
|
42
|
+
JSX_ELEMENT_PATTERN = re.compile(
|
|
43
|
+
r'<(' + '|'.join(INTERACTIVE_ELEMENTS) + r')\s+([^>]*?)(?:/>|>)',
|
|
44
|
+
re.IGNORECASE | re.DOTALL
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Pattern to check if data-cy exists
|
|
48
|
+
DATA_CY_PATTERN = re.compile(r'data-cy')
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def find_tsx_files(path: str) -> List[Path]:
|
|
52
|
+
"""Find all TSX files in the given path."""
|
|
53
|
+
root = Path(path)
|
|
54
|
+
return list(root.rglob("*.tsx"))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def analyze_file(file_path: Path) -> Dict:
|
|
58
|
+
"""Find interactive elements missing data-cy."""
|
|
59
|
+
missing = []
|
|
60
|
+
has_selector = []
|
|
61
|
+
|
|
62
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
63
|
+
content = f.read()
|
|
64
|
+
lines = content.split('\n')
|
|
65
|
+
|
|
66
|
+
# Track line numbers
|
|
67
|
+
for line_num, line in enumerate(lines, 1):
|
|
68
|
+
for match in JSX_ELEMENT_PATTERN.finditer(line):
|
|
69
|
+
element_type = match.group(1)
|
|
70
|
+
attributes = match.group(2)
|
|
71
|
+
|
|
72
|
+
element_info = {
|
|
73
|
+
'line': line_num,
|
|
74
|
+
'element': element_type,
|
|
75
|
+
'attributes': attributes[:100] + '...' if len(attributes) > 100 else attributes,
|
|
76
|
+
'context': line.strip()[:120]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if DATA_CY_PATTERN.search(attributes):
|
|
80
|
+
has_selector.append(element_info)
|
|
81
|
+
else:
|
|
82
|
+
# Skip if it has onClick/onChange but might be internal
|
|
83
|
+
if 'data-cy' not in line:
|
|
84
|
+
missing.append(element_info)
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
'file': str(file_path),
|
|
88
|
+
'missing': missing,
|
|
89
|
+
'has_selector': has_selector,
|
|
90
|
+
'coverage': len(has_selector) / (len(missing) + len(has_selector)) * 100 if (missing or has_selector) else 100
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def suggest_selector(element: str, attributes: str, file_path: str) -> str:
|
|
95
|
+
"""Suggest a selector name based on context."""
|
|
96
|
+
# Extract common attributes that might give hints
|
|
97
|
+
name_match = re.search(r'name=["\']([^"\']+)["\']', attributes)
|
|
98
|
+
id_match = re.search(r'id=["\']([^"\']+)["\']', attributes)
|
|
99
|
+
type_match = re.search(r'type=["\']([^"\']+)["\']', attributes)
|
|
100
|
+
|
|
101
|
+
# Get component name from file path
|
|
102
|
+
component = Path(file_path).stem.lower().replace('-', '.').replace('_', '.')
|
|
103
|
+
|
|
104
|
+
if name_match:
|
|
105
|
+
return f"{component}.{name_match.group(1)}"
|
|
106
|
+
if id_match:
|
|
107
|
+
return f"{component}.{id_match.group(1)}"
|
|
108
|
+
if type_match:
|
|
109
|
+
return f"{component}.{type_match.group(1)}.{element.lower()}"
|
|
110
|
+
|
|
111
|
+
return f"{component}.{element.lower()}"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def print_text_report(results: List[Dict]):
|
|
115
|
+
"""Print plain text report."""
|
|
116
|
+
total_missing = 0
|
|
117
|
+
total_with = 0
|
|
118
|
+
|
|
119
|
+
print("\n" + "=" * 70)
|
|
120
|
+
print("MISSING DATA-CY ATTRIBUTES REPORT")
|
|
121
|
+
print("=" * 70)
|
|
122
|
+
|
|
123
|
+
for result in results:
|
|
124
|
+
if result['missing']:
|
|
125
|
+
print(f"\n{result['file']}")
|
|
126
|
+
print(f"Coverage: {result['coverage']:.1f}%")
|
|
127
|
+
print("-" * 50)
|
|
128
|
+
|
|
129
|
+
for elem in result['missing']:
|
|
130
|
+
total_missing += 1
|
|
131
|
+
suggestion = suggest_selector(elem['element'], elem['attributes'], result['file'])
|
|
132
|
+
print(f" Line {elem['line']}: <{elem['element']}> missing data-cy")
|
|
133
|
+
print(f" Suggestion: data-cy={{sel('{suggestion}')}}")
|
|
134
|
+
|
|
135
|
+
total_with += len(result['has_selector'])
|
|
136
|
+
|
|
137
|
+
print("\n" + "=" * 70)
|
|
138
|
+
print(f"SUMMARY")
|
|
139
|
+
print("-" * 70)
|
|
140
|
+
print(f"Files scanned: {len(results)}")
|
|
141
|
+
print(f"Elements with data-cy: {total_with}")
|
|
142
|
+
print(f"Elements missing data-cy: {total_missing}")
|
|
143
|
+
|
|
144
|
+
if total_missing + total_with > 0:
|
|
145
|
+
coverage = total_with / (total_missing + total_with) * 100
|
|
146
|
+
print(f"Overall coverage: {coverage:.1f}%")
|
|
147
|
+
print("=" * 70 + "\n")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def print_markdown_report(results: List[Dict]):
|
|
151
|
+
"""Print markdown report."""
|
|
152
|
+
print("# Missing data-cy Attributes Report\n")
|
|
153
|
+
|
|
154
|
+
total_missing = sum(len(r['missing']) for r in results)
|
|
155
|
+
total_with = sum(len(r['has_selector']) for r in results)
|
|
156
|
+
|
|
157
|
+
print(f"**Files scanned:** {len(results)}")
|
|
158
|
+
print(f"**Elements with data-cy:** {total_with}")
|
|
159
|
+
print(f"**Elements missing data-cy:** {total_missing}\n")
|
|
160
|
+
|
|
161
|
+
for result in results:
|
|
162
|
+
if result['missing']:
|
|
163
|
+
print(f"## {result['file']}\n")
|
|
164
|
+
print(f"Coverage: {result['coverage']:.1f}%\n")
|
|
165
|
+
print("| Line | Element | Suggested Selector |")
|
|
166
|
+
print("|------|---------|-------------------|")
|
|
167
|
+
|
|
168
|
+
for elem in result['missing']:
|
|
169
|
+
suggestion = suggest_selector(elem['element'], elem['attributes'], result['file'])
|
|
170
|
+
print(f"| {elem['line']} | `<{elem['element']}>` | `{suggestion}` |")
|
|
171
|
+
|
|
172
|
+
print("")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def print_json_report(results: List[Dict]):
|
|
176
|
+
"""Print JSON report."""
|
|
177
|
+
output = {
|
|
178
|
+
'summary': {
|
|
179
|
+
'files_scanned': len(results),
|
|
180
|
+
'total_missing': sum(len(r['missing']) for r in results),
|
|
181
|
+
'total_with_selector': sum(len(r['has_selector']) for r in results),
|
|
182
|
+
},
|
|
183
|
+
'files': []
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for result in results:
|
|
187
|
+
if result['missing']:
|
|
188
|
+
file_data = {
|
|
189
|
+
'path': result['file'],
|
|
190
|
+
'coverage': result['coverage'],
|
|
191
|
+
'missing': []
|
|
192
|
+
}
|
|
193
|
+
for elem in result['missing']:
|
|
194
|
+
file_data['missing'].append({
|
|
195
|
+
'line': elem['line'],
|
|
196
|
+
'element': elem['element'],
|
|
197
|
+
'suggestion': suggest_selector(elem['element'], elem['attributes'], result['file'])
|
|
198
|
+
})
|
|
199
|
+
output['files'].append(file_data)
|
|
200
|
+
|
|
201
|
+
print(json.dumps(output, indent=2))
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def main():
|
|
205
|
+
parser = argparse.ArgumentParser(description='Find elements missing data-cy')
|
|
206
|
+
parser.add_argument('--path', required=True, help='Directory to scan')
|
|
207
|
+
parser.add_argument('--format', choices=['text', 'json', 'markdown'], default='text')
|
|
208
|
+
parser.add_argument('--output', help='Output file (default: stdout)')
|
|
209
|
+
|
|
210
|
+
args = parser.parse_args()
|
|
211
|
+
|
|
212
|
+
# Find files
|
|
213
|
+
files = find_tsx_files(args.path)
|
|
214
|
+
|
|
215
|
+
if not files:
|
|
216
|
+
print(f"No .tsx files found in {args.path}")
|
|
217
|
+
return 1
|
|
218
|
+
|
|
219
|
+
# Analyze files
|
|
220
|
+
results = [analyze_file(f) for f in files]
|
|
221
|
+
|
|
222
|
+
# Output
|
|
223
|
+
if args.output:
|
|
224
|
+
original_stdout = sys.stdout
|
|
225
|
+
sys.stdout = open(args.output, 'w')
|
|
226
|
+
|
|
227
|
+
if args.format == 'json':
|
|
228
|
+
print_json_report(results)
|
|
229
|
+
elif args.format == 'markdown':
|
|
230
|
+
print_markdown_report(results)
|
|
231
|
+
else:
|
|
232
|
+
print_text_report(results)
|
|
233
|
+
|
|
234
|
+
if args.output:
|
|
235
|
+
sys.stdout.close()
|
|
236
|
+
sys.stdout = original_stdout
|
|
237
|
+
print(f"Report written to {args.output}")
|
|
238
|
+
|
|
239
|
+
return 0
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
if __name__ == '__main__':
|
|
243
|
+
sys.exit(main())
|