@jahia/agentic 0.2.0 → 0.3.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.
Files changed (101) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +28 -0
  3. package/dist/claude/.claude/skills/jahia/SKILL.md +18 -10
  4. package/dist/claude/.claude/skills/jahia-content/SKILL.md +102 -84
  5. package/dist/claude/.claude/skills/jahia-content-create-content/SKILL.md +255 -280
  6. package/dist/claude/.claude/skills/jahia-content-explore-structure/SKILL.md +187 -96
  7. package/dist/claude/.claude/skills/jahia-content-media-upload/SKILL.md +197 -0
  8. package/dist/claude/.claude/skills/jahia-content-move-content/SKILL.md +160 -165
  9. package/dist/claude/.claude/skills/jahia-content-organize/SKILL.md +209 -0
  10. package/dist/claude/.claude/skills/jahia-content-publish/SKILL.md +181 -0
  11. package/dist/claude/.claude/skills/jahia-content-query-content/SKILL.md +122 -92
  12. package/dist/claude/.claude/skills/jahia-content-translate-content/SKILL.md +154 -225
  13. package/dist/claude/.claude/skills/jahia-dev-cypress/SKILL.md +150 -330
  14. package/dist/claude/.claude/skills/jahia-dev-query-content/SKILL.md +93 -296
  15. package/dist/claude/.claude/skills/jahia-jcr-sql2/SKILL.md +258 -0
  16. package/dist/claude/CLAUDE.md +1 -7
  17. package/dist/codex/.agents/skills/jahia/SKILL.md +18 -10
  18. package/dist/codex/.agents/skills/jahia-content/SKILL.md +102 -84
  19. package/dist/codex/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  20. package/dist/codex/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  21. package/dist/codex/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  22. package/dist/codex/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  23. package/dist/codex/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  24. package/dist/codex/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  25. package/dist/codex/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  26. package/dist/codex/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  27. package/dist/codex/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  28. package/dist/codex/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  29. package/dist/codex/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  30. package/dist/codex/AGENTS.md +2 -4
  31. package/dist/copilot/.agents/skills/jahia/SKILL.md +18 -10
  32. package/dist/copilot/.agents/skills/jahia-content/SKILL.md +102 -84
  33. package/dist/copilot/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  34. package/dist/copilot/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  35. package/dist/copilot/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  36. package/dist/copilot/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  37. package/dist/copilot/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  38. package/dist/copilot/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  39. package/dist/copilot/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  40. package/dist/copilot/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  41. package/dist/copilot/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  42. package/dist/copilot/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  43. package/dist/copilot/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  44. package/dist/copilot/AGENTS.md +2 -4
  45. package/dist/cursor/.agents/skills/jahia/SKILL.md +18 -10
  46. package/dist/cursor/.agents/skills/jahia-content/SKILL.md +102 -84
  47. package/dist/cursor/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  48. package/dist/cursor/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  49. package/dist/cursor/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  50. package/dist/cursor/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  51. package/dist/cursor/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  52. package/dist/cursor/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  53. package/dist/cursor/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  54. package/dist/cursor/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  55. package/dist/cursor/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  56. package/dist/cursor/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  57. package/dist/cursor/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  58. package/dist/gemini/.agents/skills/jahia/SKILL.md +18 -10
  59. package/dist/gemini/.agents/skills/jahia-content/SKILL.md +102 -84
  60. package/dist/gemini/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  61. package/dist/gemini/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  62. package/dist/gemini/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  63. package/dist/gemini/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  64. package/dist/gemini/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  65. package/dist/gemini/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  66. package/dist/gemini/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  67. package/dist/gemini/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  68. package/dist/gemini/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  69. package/dist/gemini/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  70. package/dist/gemini/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  71. package/dist/gemini/AGENTS.md +2 -4
  72. package/dist/gemini/GEMINI.md +2 -2
  73. package/dist/opencode/.agents/skills/jahia/SKILL.md +18 -10
  74. package/dist/opencode/.agents/skills/jahia-content/SKILL.md +102 -84
  75. package/dist/opencode/.agents/skills/jahia-content-create-content/SKILL.md +255 -280
  76. package/dist/opencode/.agents/skills/jahia-content-explore-structure/SKILL.md +187 -96
  77. package/dist/opencode/.agents/skills/jahia-content-media-upload/SKILL.md +197 -0
  78. package/dist/opencode/.agents/skills/jahia-content-move-content/SKILL.md +160 -165
  79. package/dist/opencode/.agents/skills/jahia-content-organize/SKILL.md +209 -0
  80. package/dist/opencode/.agents/skills/jahia-content-publish/SKILL.md +181 -0
  81. package/dist/opencode/.agents/skills/jahia-content-query-content/SKILL.md +122 -92
  82. package/dist/opencode/.agents/skills/jahia-content-translate-content/SKILL.md +154 -225
  83. package/dist/opencode/.agents/skills/jahia-dev-cypress/SKILL.md +150 -330
  84. package/dist/opencode/.agents/skills/jahia-dev-query-content/SKILL.md +93 -296
  85. package/dist/opencode/.agents/skills/jahia-jcr-sql2/SKILL.md +258 -0
  86. package/dist/opencode/AGENTS.md +2 -4
  87. package/dist/windsurf/.windsurf/skills/jahia/SKILL.md +18 -10
  88. package/dist/windsurf/.windsurf/skills/jahia-content/SKILL.md +102 -84
  89. package/dist/windsurf/.windsurf/skills/jahia-content-create-content/SKILL.md +255 -280
  90. package/dist/windsurf/.windsurf/skills/jahia-content-explore-structure/SKILL.md +187 -96
  91. package/dist/windsurf/.windsurf/skills/jahia-content-media-upload/SKILL.md +197 -0
  92. package/dist/windsurf/.windsurf/skills/jahia-content-move-content/SKILL.md +160 -165
  93. package/dist/windsurf/.windsurf/skills/jahia-content-organize/SKILL.md +209 -0
  94. package/dist/windsurf/.windsurf/skills/jahia-content-publish/SKILL.md +181 -0
  95. package/dist/windsurf/.windsurf/skills/jahia-content-query-content/SKILL.md +122 -92
  96. package/dist/windsurf/.windsurf/skills/jahia-content-translate-content/SKILL.md +154 -225
  97. package/dist/windsurf/.windsurf/skills/jahia-dev-cypress/SKILL.md +150 -330
  98. package/dist/windsurf/.windsurf/skills/jahia-dev-query-content/SKILL.md +93 -296
  99. package/dist/windsurf/.windsurf/skills/jahia-jcr-sql2/SKILL.md +258 -0
  100. package/dist/windsurf/AGENTS.md +2 -4
  101. package/package.json +3 -3
@@ -1,25 +1,24 @@
1
1
  ---
2
2
  name: jahia-dev-cypress
3
- description: Write and scaffold Cypress e2e tests for a Jahia JS template set. Covers directory setup, cypress.config.ts, @jahia/cypress commands, site creation/teardown, content seeding via addNode, component rendering assertions, CSS Module class selectors, and the three mandatory spec files per component.
3
+ description: Write and scaffold Cypress e2e tests for a Jahia JS template set. Covers test-package structure, site seeding, isolation, content setup, rendering assertions, and stable selector strategy.
4
4
  ---
5
5
 
6
- # SKILL — Jahia Cypress E2E Tests
6
+ # Skill: jahia-dev-cypress
7
7
 
8
- ## When this skill applies
9
-
10
- A new component, content type, or page template has been built in a JS template set and needs Cypress coverage. Per the CTO guidelines, tests ship alongside the component — not retroactively.
8
+ Use this skill when a Jahia JS template set needs Cypress end-to-end coverage.
9
+ Tests should ship with the component or page template, not weeks later.
11
10
 
12
11
  ---
13
12
 
14
13
  ## Test directory layout
15
14
 
16
- Tests live in a **separate `tests/` directory** at the module root — never inside `src/`.
15
+ Tests live in a separate `tests/` directory at the module root.
17
16
 
18
17
  ```
19
18
  <module>/
20
- ├── src/ ← component code
19
+ ├── src/
21
20
  ├── settings/
22
- ├── tests/ ← Cypress project (separate package)
21
+ ├── tests/
23
22
  │ ├── package.json
24
23
  │ ├── cypress.config.ts
25
24
  │ ├── .env.example
@@ -32,42 +31,41 @@ Tests live in a **separate `tests/` directory** at the module root — never ins
32
31
  │ │ ├── fixtures/
33
32
  │ │ │ └── graphql/
34
33
  │ │ │ └── mutation/
35
- │ │ │ └── *.graphql
36
34
  │ │ ├── plugins/
37
35
  │ │ │ └── index.js
38
36
  │ │ └── support/
39
37
  │ │ ├── commands.ts
40
- │ │ └── constants.ts
38
+ │ │ ├── constants.ts
39
+ │ │ └── e2e.ts
41
40
  │ └── results/
42
- │ └── .gitignore
43
41
  └── package.json
44
42
  ```
45
43
 
46
44
  ---
47
45
 
48
- ## Step 1 — Scaffold the test package
46
+ ## Step 1 — Scaffold the Cypress package
49
47
 
50
48
  Create `tests/package.json`:
51
49
 
52
50
  ```json
53
51
  {
54
- "name": "<module-name>-cypress",
55
- "private": true,
56
- "scripts": {
57
- "e2e:ci": "cypress run",
58
- "e2e:debug": "cypress open"
59
- },
60
- "devDependencies": {
61
- "@jahia/cypress": "^7.1.0",
62
- "cypress": "^14.0.0",
63
- "cypress-terminal-report": "^5.3.12",
64
- "typescript": "^5.0.0"
65
- },
66
- "packageManager": "yarn@4.12.0"
52
+ "name": "<module-name>-cypress",
53
+ "private": true,
54
+ "scripts": {
55
+ "e2e:ci": "cypress run",
56
+ "e2e:debug": "cypress open"
57
+ },
58
+ "devDependencies": {
59
+ "@jahia/cypress": "^7.1.0",
60
+ "cypress": "^14.0.0",
61
+ "cypress-terminal-report": "^5.3.12",
62
+ "typescript": "^5.0.0"
63
+ },
64
+ "packageManager": "yarn@4.12.0"
67
65
  }
68
66
  ```
69
67
 
70
- Install:
68
+ Install with:
71
69
 
72
70
  ```bash
73
71
  cd tests && yarn install
@@ -75,371 +73,193 @@ cd tests && yarn install
75
73
 
76
74
  ---
77
75
 
78
- ## Step 2 — cypress.config.ts
76
+ ## Step 2 — Configure Cypress
79
77
 
80
78
  ```typescript
81
- // tests/cypress.config.ts
82
- import { defineConfig } from 'cypress'
83
- import * as fs from 'node:fs'
79
+ import { defineConfig } from 'cypress';
80
+ import * as fs from 'node:fs';
84
81
 
85
82
  export default defineConfig({
86
- chromeWebSecurity: false,
87
- defaultCommandTimeout: 10000,
88
- requestTimeout: 300000, // 5 min — site provisioning is slow
89
- responseTimeout: 300000,
90
- viewportWidth: 1366,
91
- viewportHeight: 768,
92
- watchForFileChanges: false,
93
- screenshotsFolder: './results/screenshots',
94
- videosFolder: './results/videos',
95
- e2e: {
96
- baseUrl: 'http://localhost:8080',
97
- specPattern: ['**/**.cy.ts'],
98
- setupNodeEvents(on, config) {
99
- require('@jahia/cypress/dist/plugins/registerPlugins').registerPlugins(on, config)
100
- require('cypress-terminal-report/src/installLogsPrinter')(on, {
101
- printLogsToConsole: 'onFail',
102
- outputRoot: config.projectRoot + '/results/',
103
- })
104
- on('task', {
105
- readFileMaybe(filename) {
106
- if (fs.existsSync(filename)) return fs.readFileSync(filename, 'utf8')
107
- return null
108
- },
109
- })
110
- return config
83
+ chromeWebSecurity: false,
84
+ defaultCommandTimeout: 10000,
85
+ requestTimeout: 300000,
86
+ responseTimeout: 300000,
87
+ viewportWidth: 1366,
88
+ viewportHeight: 768,
89
+ watchForFileChanges: false,
90
+ screenshotsFolder: './results/screenshots',
91
+ videosFolder: './results/videos',
92
+ e2e: {
93
+ baseUrl: 'http://localhost:8080',
94
+ specPattern: ['**/**.cy.ts'],
95
+ setupNodeEvents(on, config) {
96
+ require('@jahia/cypress/dist/plugins/registerPlugins').registerPlugins(on, config);
97
+ require('cypress-terminal-report/src/installLogsPrinter')(on, {
98
+ printLogsToConsole: 'onFail',
99
+ outputRoot: config.projectRoot + '/results/',
100
+ });
101
+ on('task', {
102
+ readFileMaybe(filename) {
103
+ if (fs.existsSync(filename)) return fs.readFileSync(filename, 'utf8');
104
+ return null;
111
105
  },
106
+ });
107
+ return config;
112
108
  },
113
- })
109
+ },
110
+ });
114
111
  ```
115
112
 
116
113
  ---
117
114
 
118
- ## Step 3 — constants.ts
115
+ ## Step 3 — Keep tests isolated
116
+
117
+ ### Site-per-suite isolation
118
+
119
+ Every suite should create its own site in `before()` and delete it in `after()`.
120
+ Do not rely on pre-existing sites.
119
121
 
120
122
  ```typescript
121
- // tests/cypress/support/constants.ts
122
- export const SITE_KEY = '<module-name>-tests'
123
- export const TEMPLATE_SET = '<module-name>' // matches module name in package.json
123
+ import { createSite, deleteSite } from '@jahia/cypress';
124
+
125
+ describe('Hero banner', () => {
126
+ const SITE_KEY = 'hero-banner-tests';
127
+
128
+ before(() => {
129
+ cy.login();
130
+ createSite(SITE_KEY, {
131
+ templateSet: 'my-module',
132
+ locale: 'en',
133
+ languages: 'en,fr',
134
+ serverName: 'localhost',
135
+ });
136
+ cy.logout();
137
+ });
138
+
139
+ after(() => {
140
+ cy.login();
141
+ deleteSite(SITE_KEY);
142
+ cy.logout();
143
+ });
144
+ });
124
145
  ```
125
146
 
147
+ ### Session hygiene
148
+
149
+ Use `beforeEach` / `afterEach` for login boundaries when the scenario needs authentication.
150
+
126
151
  ---
127
152
 
128
- ## Step 4 — The three mandatory spec files
153
+ ## Step 4 — Keep the three mandatory spec files
129
154
 
130
- Every component ships exactly these three files. Adapt to the component under test.
155
+ Every component test folder should contain:
131
156
 
132
- ### `happy-path.cy.ts` — component renders correctly with valid content
157
+ - `happy-path.cy.ts`
158
+ - `authorization.cy.ts`
159
+ - `edge-cases.cy.ts`
133
160
 
134
- ```typescript
135
- import { addNode, createSite, deleteSite, publishAndWaitJobEnding } from '@jahia/cypress'
136
- import { SITE_KEY, TEMPLATE_SET } from '../../support/constants'
137
-
138
- describe('<ComponentName> — happy path', () => {
139
- before(() => {
140
- cy.login()
141
- createSite(SITE_KEY, {
142
- templateSet: TEMPLATE_SET,
143
- locale: 'en',
144
- languages: 'en,fr',
145
- serverName: 'localhost',
146
- })
147
-
148
- // Seed a page with a test component
149
- addNode({
150
- parentPathOrId: `/sites/${SITE_KEY}/home`,
151
- name: 'testPage',
152
- primaryNodeType: 'jnt:page',
153
- properties: [
154
- { name: 'jcr:title', value: 'Test Page', language: 'en' },
155
- { name: 'j:templateName', value: 'simple' },
156
- ],
157
- }).then(() => {
158
- addNode({
159
- parentPathOrId: `/sites/${SITE_KEY}/home/testPage/pagecontent`,
160
- name: 'myComponent',
161
- primaryNodeType: 'ns:myComponentType',
162
- properties: [
163
- { name: 'title', value: 'Hello World', language: 'en' },
164
- { name: 'subtitle', value: 'A subtitle', language: 'en' },
165
- ],
166
- })
167
- })
168
-
169
- publishAndWaitJobEnding(`/sites/${SITE_KEY}`, ['en', 'fr'])
170
- cy.logout()
171
- })
172
-
173
- after(() => {
174
- cy.login()
175
- deleteSite(SITE_KEY)
176
- cy.logout()
177
- })
178
-
179
- beforeEach(() => cy.login())
180
- afterEach(() => cy.logout())
181
-
182
- it('renders the component in live mode', () => {
183
- cy.visit(`/sites/${SITE_KEY}/home/testPage.html`)
184
- cy.get('[data-testid="my-component"]').should('be.visible')
185
- cy.contains('Hello World')
186
- })
187
-
188
- it('renders the component in FR locale', () => {
189
- cy.visit(`/fr/sites/${SITE_KEY}/home/testPage.html`)
190
- cy.get('[data-testid="my-component"]').should('be.visible')
191
- })
192
-
193
- it('renders the component in preview mode', () => {
194
- cy.visit(`/cms/render/default/en/sites/${SITE_KEY}/home/testPage.html`)
195
- cy.get('[data-testid="my-component"]').should('be.visible')
196
- })
197
- })
198
- ```
161
+ ### Happy path
199
162
 
200
- ### `authorization.cy.ts` access control
163
+ Validate the component renders correctly in live, preview, and at least one secondary locale.
201
164
 
202
- ```typescript
203
- import { addNode, createSite, deleteSite, publishAndWaitJobEnding } from '@jahia/cypress'
204
- import { SITE_KEY, TEMPLATE_SET } from '../../support/constants'
205
-
206
- describe('<ComponentName> — authorization', () => {
207
- before(() => {
208
- cy.login()
209
- createSite(SITE_KEY, {
210
- templateSet: TEMPLATE_SET,
211
- locale: 'en',
212
- serverName: 'localhost',
213
- })
214
- publishAndWaitJobEnding(`/sites/${SITE_KEY}`, ['en'])
215
- cy.logout()
216
- })
217
-
218
- after(() => {
219
- cy.login()
220
- deleteSite(SITE_KEY)
221
- cy.logout()
222
- })
223
-
224
- it('live page is publicly accessible without login', () => {
225
- // No cy.login() — verify anonymous access
226
- cy.visit(`/sites/${SITE_KEY}/home.html`)
227
- cy.get('body').should('be.visible')
228
- // Should NOT redirect to login
229
- cy.url().should('not.include', '/login')
230
- })
231
-
232
- it('default workspace requires authentication', () => {
233
- cy.visit(`/cms/render/default/en/sites/${SITE_KEY}/home.html`)
234
- // Unauthenticated → redirected to login or 401
235
- cy.url().should('satisfy', (url: string) =>
236
- url.includes('/login') || url.includes('/logout')
237
- )
238
- })
239
-
240
- it('admin can access jcontent for the site', () => {
241
- cy.login()
242
- cy.visit(`/jahia/jcontent/${SITE_KEY}/en/pages`)
243
- cy.get('body').should('be.visible')
244
- cy.url().should('not.include', '/login')
245
- cy.logout()
246
- })
247
- })
248
- ```
165
+ ### Authorization
249
166
 
250
- ### `edge-cases.cy.ts` empty values, missing optional fields, boundary conditions
167
+ Validate anonymous versus authenticated access where relevant.
251
168
 
252
- ```typescript
253
- import { addNode, createSite, deleteSite, publishAndWaitJobEnding } from '@jahia/cypress'
254
- import { SITE_KEY, TEMPLATE_SET } from '../../support/constants'
255
-
256
- describe('<ComponentName> — edge cases', () => {
257
- before(() => {
258
- cy.login()
259
- createSite(SITE_KEY, {
260
- templateSet: TEMPLATE_SET,
261
- locale: 'en',
262
- serverName: 'localhost',
263
- })
264
-
265
- // Seed a component with only mandatory fields — all optionals missing
266
- addNode({
267
- parentPathOrId: `/sites/${SITE_KEY}/home`,
268
- name: 'testPage',
269
- primaryNodeType: 'jnt:page',
270
- properties: [
271
- { name: 'jcr:title', value: 'Edge Case Page', language: 'en' },
272
- { name: 'j:templateName', value: 'simple' },
273
- ],
274
- }).then(() => {
275
- addNode({
276
- parentPathOrId: `/sites/${SITE_KEY}/home/testPage/pagecontent`,
277
- name: 'emptyComponent',
278
- primaryNodeType: 'ns:myComponentType',
279
- // Only mandatory properties — no optional ones
280
- properties: [
281
- { name: 'title', value: 'Minimal', language: 'en' },
282
- ],
283
- })
284
- })
285
-
286
- publishAndWaitJobEnding(`/sites/${SITE_KEY}`, ['en'])
287
- cy.logout()
288
- })
289
-
290
- after(() => {
291
- cy.login()
292
- deleteSite(SITE_KEY)
293
- cy.logout()
294
- })
295
-
296
- it('renders without errors when optional fields are empty', () => {
297
- cy.visit(`/sites/${SITE_KEY}/home/testPage.html`)
298
- // No JS errors
299
- cy.on('uncaught:exception', () => false) // log but don't fail on known framework noise
300
- cy.get('[data-testid="my-component"]').should('exist')
301
- // Optional subtitle should not render a broken element
302
- cy.get('[data-testid="subtitle"]').should('not.exist')
303
- })
304
-
305
- it('page does not return 500 for any seeded content', () => {
306
- cy.request(`/sites/${SITE_KEY}/home/testPage.html`).its('status').should('eq', 200)
307
- })
308
- })
309
- ```
169
+ ### Edge cases
170
+
171
+ Seed only mandatory fields and verify missing optional values do not cause broken renders or 500s.
310
172
 
311
173
  ---
312
174
 
313
- ## Step 5 — Seeding content via GraphQL (alternative to `addNode`)
314
-
315
- For complex mutations (adding mixins, setting weakreference properties, bulk batches), use `.graphql` fixture files:
316
-
317
- ```graphql
318
- # tests/cypress/fixtures/graphql/mutation/seedMyComponent.graphql
319
- mutation seedMyComponent($parentPath: String!, $imageUuid: String!) {
320
- jcr {
321
- mutateNode(pathOrId: $parentPath) {
322
- addChild(
323
- name: "myComponent"
324
- primaryNodeType: "ns:myComponentType"
325
- properties: [
326
- { name: "title", value: "Test Title", language: "en" }
327
- { name: "image", type: WEAKREFERENCE, value: $imageUuid }
328
- ]
329
- ) { uuid }
330
- }
331
- }
332
- }
175
+ ## Step 5 — Seed content predictably
176
+
177
+ Use `createSite`, `addNode`, and `publishAndWaitJobEnding` from `@jahia/cypress`.
178
+
179
+ ```typescript
180
+ import { addNode, createSite, deleteSite, publishAndWaitJobEnding } from '@jahia/cypress';
181
+
182
+ addNode({
183
+ parentPathOrId: `/sites/${SITE_KEY}/home/testPage/pagecontent`,
184
+ name: 'myComponent',
185
+ primaryNodeType: 'ns:myComponentType',
186
+ properties: [
187
+ { name: 'title', value: 'Hello World', language: 'en' },
188
+ ],
189
+ });
190
+
191
+ publishAndWaitJobEnding(`/sites/${SITE_KEY}`, ['en']);
333
192
  ```
334
193
 
335
- Call from a test:
194
+ Prefer fixture files over long inline GraphQL strings when seeding becomes complex.
336
195
 
337
196
  ```typescript
338
197
  cy.apollo({
339
- mutationFile: 'graphql/mutation/seedMyComponent.graphql',
340
- variables: { parentPath: `/sites/${SITE_KEY}/home/testPage/pagecontent`, imageUuid },
341
- })
198
+ mutationFile: 'graphql/mutation/seedMyComponent.graphql',
199
+ variables: { parentPath: `/sites/${SITE_KEY}/home/testPage/pagecontent` },
200
+ });
342
201
  ```
343
202
 
344
203
  ---
345
204
 
346
- ## Step 6 — Targeting CSS Module classes
205
+ ## Step 6 — Use stable selectors
347
206
 
348
- CSS Modules hash class names at build time. Never target `.myClass` directly — use the `[class*=]` attribute selector to match the unhashed portion:
207
+ CSS Modules hash class names, so do not target final generated class names directly.
349
208
 
350
209
  ```typescript
351
- // Fragile class name is hashed
352
- cy.get('.card_abc123').should('exist')
210
+ // Better: add data-testid in the component
211
+ cy.get('[data-testid="hero-banner"]').should('be.visible');
353
212
 
354
- // Stable matches the un-hashed segment
355
- cy.get('[class*="_card_"]').should('exist')
356
- cy.get('[class*="_card_"]').first().click()
357
-
358
- // ✅ Better — add data-testid to the component and target that
359
- cy.get('[data-testid="hero-banner"]').should('be.visible')
213
+ // Acceptable fallback for CSS Modules
214
+ cy.get('[class*="_card_"]').first().click();
360
215
  ```
361
216
 
362
- **Preferred approach**: add `data-testid` attributes to component root elements in production views. They survive CSS Module renaming and minification:
217
+ Preferred approach in production views:
363
218
 
364
219
  ```tsx
365
- // In default.server.tsx
366
220
  <section data-testid="hero-banner" className={styles.hero}>
367
221
  ```
368
222
 
369
223
  ---
370
224
 
371
- ## Step 7 — Locale assertions
225
+ ## Step 7 — Organize support code cleanly
372
226
 
373
- Always test at least EN and FR. Use `cy.visit` with the locale prefix for FR:
227
+ - Keep `cypress/support/commands.ts` focused on command registration.
228
+ - Move larger helper implementations into small modules under `cypress/support/`.
229
+ - Put run-level cleanup in `support/e2e.ts` so repeated runs stay idempotent.
230
+ - Avoid large copy-pasted setup blocks across spec files.
374
231
 
375
- ```typescript
376
- // EN (default)
377
- cy.visit(`/sites/${SITE_KEY}/home/testPage.html`)
232
+ ---
378
233
 
379
- // FR
380
- cy.visit(`/fr/sites/${SITE_KEY}/home/testPage.html`)
234
+ ## Guardrails
381
235
 
382
- // Assert locale-specific content
383
- cy.contains('Découvrir plus') // FR CTA label
384
- ```
236
+ 1. Never test against production data — always create isolated sites and fixtures.
237
+ 2. Always clean up created sites and users in `after()` hooks.
238
+ 3. Publish seeded content before asserting live renders.
239
+ 4. Prefer `data-testid` selectors over CSS classes.
240
+ 5. Avoid flaky `cy.wait(ms)` calls — use polling helpers or explicit conditions instead.
241
+ 6. Test both the happy path and the main failure/empty-state paths.
242
+ 7. Verify at least one secondary locale such as FR when the component is localized.
385
243
 
386
244
  ---
387
245
 
388
- ## `@jahia/cypress` API reference
389
-
390
- | Function | Import | Purpose |
391
- |---|---|---|
392
- | `cy.login()` | built-in command | Log in as root (uses `SUPER_USER_PASSWORD` env var, default `root1234`) |
393
- | `cy.logout()` | built-in command | Log out |
394
- | `cy.apollo({ mutationFile, variables })` | built-in command | Execute a GraphQL mutation from a fixture file |
395
- | `cy.runProvisioningScript({ script })` | built-in command | Run a Jahia provisioning YAML |
396
- | `createSite(siteKey, options)` | `@jahia/cypress` | Create a site programmatically |
397
- | `deleteSite(siteKey)` | `@jahia/cypress` | Delete a site (use in `after`) |
398
- | `addNode(variables)` | `@jahia/cypress` | Create a JCR node via GraphQL |
399
- | `publishAndWaitJobEnding(path, locales?)` | `@jahia/cypress` | Publish content and block until the job completes |
400
- | `uploadFile(path, target, name, mimeType)` | `@jahia/cypress` | Upload a file to the JCR |
246
+ ## Running tests
401
247
 
402
- `createSite` options:
248
+ ```bash
249
+ # From the module root
250
+ yarn build && yarn jahia-deploy
403
251
 
404
- ```typescript
405
- createSite(SITE_KEY, {
406
- templateSet: 'my-module', // module name
407
- locale: 'en', // default locale
408
- languages: 'en,fr', // all activated locales (comma-separated)
409
- serverName: 'localhost',
410
- })
252
+ # Then run Cypress
253
+ cd tests && yarn e2e:ci
411
254
  ```
412
255
 
413
- ---
414
-
415
- ## Common pitfalls
416
-
417
- | Pitfall | Consequence |
418
- |---|---|
419
- | Missing `publishAndWaitJobEnding` after seeding | Tests run against unpublished content; live page returns stale or empty render |
420
- | Targeting hashed CSS Module class names directly | Tests break on every rebuild |
421
- | Skipping FR assertions | i18n regressions go undetected |
422
- | No `after` hook to delete the site | Test sites accumulate on the Jahia instance and pollute subsequent runs |
423
- | Running `createSite` without first deleting — no teardown in prior run | Site creation fails because the key already exists |
424
- | Relying on a pre-existing site instead of seeding | Tests are not self-contained and fail on a clean Jahia instance |
425
- | Not wrapping login/logout in `beforeEach`/`afterEach` | Session bleeds across tests; order-dependent failures |
256
+ Use `yarn e2e:debug` locally when you need the interactive runner.
426
257
 
427
258
  ---
428
259
 
429
- ## Running tests
430
-
431
- ```bash
432
- # Against local Jahia (http://localhost:8080)
433
- cd tests
434
- yarn e2e:debug # opens Cypress UI
435
- yarn e2e:ci # headless, for CI
436
- ```
260
+ ## Related skills
437
261
 
438
- Jahia must be running and the module deployed before running tests. Deploy first:
262
+ - `/jahia-dev-build-component` build the component being tested
263
+ - `/jahia-dev-accessibility` — catch semantic and keyboard issues before or after writing e2e tests
264
+ - `/jahia-dev-create-page-template` — create the page templates that Cypress will exercise
439
265
 
440
- ```bash
441
- # From module root
442
- yarn build && yarn jahia-deploy
443
- # Then run tests
444
- cd tests && yarn e2e:ci
445
- ```